diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.cs index 486aeb72faddb..a49ffaf9378b3 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.cs @@ -553,7 +553,7 @@ internal struct ArrayElement [StackTraceHidden] [DebuggerStepThrough] [MethodImpl(MethodImplOptions.AggressiveOptimization)] - private static ref object? LdelemaRef(Array array, int index, void* type) + private static ref object? LdelemaRef(Array array, nint index, void* type) { // this will throw appropriate exceptions if array is null or access is out of range. ref object? element = ref Unsafe.As(array)[index].Value; @@ -569,7 +569,7 @@ internal struct ArrayElement [StackTraceHidden] [DebuggerStepThrough] [MethodImpl(MethodImplOptions.AggressiveOptimization)] - private static void StelemRef(Array array, int index, object? obj) + private static void StelemRef(Array array, nint index, object? obj) { // this will throw appropriate exceptions if array is null or access is out of range. ref object? element = ref Unsafe.As(array)[index].Value; diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h index 9a6cbc053e1ce..2a9fc7d7b044b 100644 --- a/src/coreclr/inc/jiteeversionguid.h +++ b/src/coreclr/inc/jiteeversionguid.h @@ -43,11 +43,11 @@ typedef const GUID *LPCGUID; #define GUID_DEFINED #endif // !GUID_DEFINED -constexpr GUID JITEEVersionIdentifier = { /* f2faa5fc-a1ec-4244-aebb-5597bfd7153a */ - 0xf2faa5fc, - 0xa1ec, - 0x4244, - {0xae, 0xbb, 0x55, 0x97, 0xbf, 0xd7, 0x15, 0x3a} +constexpr GUID JITEEVersionIdentifier = { /* 0d853657-7a01-421f-b1b0-d22a8e691441 */ + 0x0d853657, + 0x7a01, + 0x421f, + {0xb1, 0xb0, 0xd2, 0x2a, 0x8e, 0x69, 0x14, 0x41} }; ////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/coreclr/inc/readytorun.h b/src/coreclr/inc/readytorun.h index 5a7b798d084e6..4ba409d849952 100644 --- a/src/coreclr/inc/readytorun.h +++ b/src/coreclr/inc/readytorun.h @@ -15,8 +15,8 @@ #define READYTORUN_SIGNATURE 0x00525452 // 'RTR' // Keep these in sync with src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs -#define READYTORUN_MAJOR_VERSION 0x0006 -#define READYTORUN_MINOR_VERSION 0x0003 +#define READYTORUN_MAJOR_VERSION 0x0007 +#define READYTORUN_MINOR_VERSION 0x0000 #define MINIMUM_READYTORUN_MAJOR_VERSION 0x006 @@ -401,6 +401,10 @@ enum ReadyToRunHelper READYTORUN_HELPER_StackProbe = 0x111, READYTORUN_HELPER_GetCurrentManagedThreadId = 0x112, + + // Array helpers for use with native ints + READYTORUN_HELPER_Stelem_Ref_I = 0x113, + READYTORUN_HELPER_Ldelema_Ref_I = 0x114, }; #include "readytoruninstructionset.h" diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 3caad0781ac9d..0df2798db15c4 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -13717,7 +13717,24 @@ void Compiler::impImportBlockCode(BasicBlock* block) GenTree* type = op1; GenTree* index = impPopStack().val; GenTree* arr = impPopStack().val; - op1 = gtNewHelperCallNode(CORINFO_HELP_LDELEMA_REF, TYP_BYREF, arr, index, type); +#ifdef TARGET_64BIT + // The CLI Spec allows an array to be indexed by either an int32 or a native int. + // The array helper takes a native int for array length. + // So if we have an int, explicitly extend it to be a native int. + if (genActualType(index->TypeGet()) != TYP_I_IMPL) + { + if (index->IsIntegralConst()) + { + index->gtType = TYP_I_IMPL; + } + else + { + bool isUnsigned = false; + index = gtNewCastNode(TYP_I_IMPL, index, isUnsigned, TYP_I_IMPL); + } + } +#endif // TARGET_64BIT + op1 = gtNewHelperCallNode(CORINFO_HELP_LDELEMA_REF, TYP_BYREF, arr, index, type); } impPushOnStack(op1, tiRetVal); @@ -13858,7 +13875,24 @@ void Compiler::impImportBlockCode(BasicBlock* block) impPopStack(3); - // Else call a helper function to do the assignment +// Else call a helper function to do the assignment +#ifdef TARGET_64BIT + // The CLI Spec allows an array to be indexed by either an int32 or a native int. + // The array helper takes a native int for array length. + // So if we have an int, explicitly extend it to be a native int. + if (genActualType(index->TypeGet()) != TYP_I_IMPL) + { + if (index->IsIntegralConst()) + { + index->gtType = TYP_I_IMPL; + } + else + { + bool isUnsigned = false; + index = gtNewCastNode(TYP_I_IMPL, index, isUnsigned, TYP_I_IMPL); + } + } +#endif // TARGET_64BIT op1 = gtNewHelperCallNode(CORINFO_HELP_ARRADDR_ST, TYP_VOID, array, index, value); goto SPILL_APPEND; } diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/CompilerServices/Unsafe.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/CompilerServices/Unsafe.cs index cd672bc4192e6..7c77d12b7cc38 100644 --- a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/CompilerServices/Unsafe.cs +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/CompilerServices/Unsafe.cs @@ -91,6 +91,15 @@ public static ref T Add(ref T source, int elementOffset) return ref AddByteOffset(ref source, (IntPtr)(elementOffset * (nint)SizeOf())); } + /// + /// Adds an element offset to the given reference. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref T Add(ref T source, IntPtr elementOffset) + { + return ref AddByteOffset(ref source, (IntPtr)((nint)elementOffset * (nint)SizeOf())); + } + /// /// Reinterprets the given location as a reference to a value of type . /// diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/TypeCast.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/TypeCast.cs index ebf957a22d0b8..7cd42c6ef6bd8 100644 --- a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/TypeCast.cs +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/TypeCast.cs @@ -743,7 +743,7 @@ internal struct ArrayElement // Array stelem/ldelema helpers with RyuJIT conventions // [RuntimeExport("RhpStelemRef")] - public static unsafe void StelemRef(Array array, int index, object obj) + public static unsafe void StelemRef(Array array, nint index, object obj) { // This is supported only on arrays Debug.Assert(array.GetMethodTable()->IsArray, "first argument must be an array"); @@ -814,7 +814,7 @@ private static unsafe void StelemRef_Helper(ref object element, MethodTable* ele } [RuntimeExport("RhpLdelemaRef")] - public static unsafe ref object LdelemaRef(Array array, int index, IntPtr elementType) + public static unsafe ref object LdelemaRef(Array array, nint index, IntPtr elementType) { Debug.Assert(array.GetMethodTable()->IsArray, "first argument must be an array"); diff --git a/src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h b/src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h index 1c22d33890d5c..8600b4c5913c1 100644 --- a/src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h +++ b/src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h @@ -10,7 +10,7 @@ struct ReadyToRunHeaderConstants { static const uint32_t Signature = 0x00525452; // 'RTR' - static const uint32_t CurrentMajorVersion = 6; + static const uint32_t CurrentMajorVersion = 7; static const uint32_t CurrentMinorVersion = 0; }; diff --git a/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs b/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs index b0ab3abf7bc1b..ea705d1f6f34a 100644 --- a/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs +++ b/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs @@ -14,8 +14,8 @@ internal struct ReadyToRunHeaderConstants { public const uint Signature = 0x00525452; // 'RTR' - public const ushort CurrentMajorVersion = 6; - public const ushort CurrentMinorVersion = 3; + public const ushort CurrentMajorVersion = 7; + public const ushort CurrentMinorVersion = 0; } #pragma warning disable 0169 diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index cd4995aa32141..621cbf5b86fd9 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -1226,8 +1226,8 @@ DEFINE_METHOD(CASTHELPERS, CHKCASTINTERFACE, ChkCastInterface, SM_Ptr DEFINE_METHOD(CASTHELPERS, CHKCASTCLASS, ChkCastClass, SM_PtrVoid_Obj_RetObj) DEFINE_METHOD(CASTHELPERS, CHKCASTCLASSSPECIAL, ChkCastClassSpecial, SM_PtrVoid_Obj_RetObj) DEFINE_METHOD(CASTHELPERS, UNBOX, Unbox, SM_PtrVoid_Obj_RetRefByte) -DEFINE_METHOD(CASTHELPERS, STELEMREF, StelemRef, SM_Array_Int_Obj_RetVoid) -DEFINE_METHOD(CASTHELPERS, LDELEMAREF, LdelemaRef, SM_Array_Int_PtrVoid_RetRefObj) +DEFINE_METHOD(CASTHELPERS, STELEMREF, StelemRef, SM_Array_IntPtr_Obj_RetVoid) +DEFINE_METHOD(CASTHELPERS, LDELEMAREF, LdelemaRef, SM_Array_IntPtr_PtrVoid_RetRefObj) DEFINE_CLASS_U(System, GCMemoryInfoData, GCMemoryInfoData) DEFINE_FIELD_U(_highMemoryLoadThresholdBytes, GCMemoryInfoData, highMemLoadThresholdBytes) diff --git a/src/coreclr/vm/metasig.h b/src/coreclr/vm/metasig.h index 9653fc2967e46..5b5bfc2d42746 100644 --- a/src/coreclr/vm/metasig.h +++ b/src/coreclr/vm/metasig.h @@ -604,6 +604,8 @@ DEFINE_METASIG(GM(RetT, IMAGE_CEE_CS_CALLCONV_DEFAULT, 1, _, M(0))) DEFINE_METASIG_T(SM(Array_Int_Array_Int_Int_RetVoid, C(ARRAY) i C(ARRAY) i i, v)) DEFINE_METASIG_T(SM(Array_Int_Obj_RetVoid, C(ARRAY) i j, v)) DEFINE_METASIG_T(SM(Array_Int_PtrVoid_RetRefObj, C(ARRAY) i P(v), r(j))) +DEFINE_METASIG_T(SM(Array_IntPtr_Obj_RetVoid, C(ARRAY) I j, v)) +DEFINE_METASIG_T(SM(Array_IntPtr_PtrVoid_RetRefObj, C(ARRAY) I P(v), r(j))) DEFINE_METASIG(SM(Obj_IntPtr_Bool_RetVoid, j I F, v)) DEFINE_METASIG(SM(IntPtr_Obj_RetVoid, I j, v)) diff --git a/src/tests/JIT/Directed/Arrays/nintindexoutofrange.cs b/src/tests/JIT/Directed/Arrays/nintindexoutofrange.cs new file mode 100644 index 0000000000000..bbfe3b0eda59d --- /dev/null +++ b/src/tests/JIT/Directed/Arrays/nintindexoutofrange.cs @@ -0,0 +1,66 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +using System; +using System.Runtime.CompilerServices; + +class NintIndexOutOfRangeTest +{ + [MethodImpl(MethodImplOptions.NoInlining)] + static void Stelem_Ref(object[] arr, nint i, Object value) + => arr[i] = value; + + [MethodImpl(MethodImplOptions.NoInlining)] + static void LdElemATestHelper(ref object nothingOfInterest) + {} + + [MethodImpl(MethodImplOptions.NoInlining)] + static void LdElemA(object[] arr, nint i) + { + LdElemATestHelper(ref arr[i]); + } + + public static unsafe int Main() + { + long longIndex = ((long)1) << 32; + nint index = (nint)longIndex; + bool failed = false; + + // On a 32bit platform, just succeed. + if (sizeof(long) != sizeof(nint)) + return 100; + + var arr = new Object[10]; + // Try store to invalid index with null + try + { + Stelem_Ref(arr, index, null); + failed = true; + Console.WriteLine("Failed to throw IndexOutOfRange when storing null"); + } + catch (IndexOutOfRangeException) {} + + // Try store to invalid index with actual value + try + { + Stelem_Ref(arr, index, new object()); + failed = true; + Console.WriteLine("Failed to throw IndexOutOfRange when storing object"); + } + catch (IndexOutOfRangeException) {} + + // Try to load element address + try + { + LdElemA(arr, index); + failed = true; + Console.WriteLine("Failed to throw IndexOutOfRange when accessing element"); + } + catch (IndexOutOfRangeException) {} + + if (failed) + return 1; + else + return 100; + } +} diff --git a/src/tests/JIT/Directed/Arrays/nintindexoutofrange.csproj b/src/tests/JIT/Directed/Arrays/nintindexoutofrange.csproj new file mode 100644 index 0000000000000..78d2d2e26fe19 --- /dev/null +++ b/src/tests/JIT/Directed/Arrays/nintindexoutofrange.csproj @@ -0,0 +1,12 @@ + + + Exe + 0 + + + true + + + + + diff --git a/src/tests/issues.targets b/src/tests/issues.targets index d2aa958209981..992e46228268f 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -1454,6 +1454,9 @@ + + https://github.com/dotnet/runtime/issues/71656 + https://github.com/dotnet/runtime/issues/61693