diff --git a/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs index c68bc6aa5d3ec0..539b10e2aa02ee 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs @@ -671,18 +671,11 @@ public static unsafe T[] AllocateUninitializedArray(int length, bool pinned = ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T)); } - // kept outside of the small arrays hot path to have inlining without big size growth - return AllocateNewUninitializedArray(length, pinned); - - // remove the local function when https://github.com/dotnet/runtime/issues/5973 is implemented - static T[] AllocateNewUninitializedArray(int length, bool pinned) - { - GC_ALLOC_FLAGS flags = GC_ALLOC_FLAGS.GC_ALLOC_ZEROING_OPTIONAL; - if (pinned) - flags |= GC_ALLOC_FLAGS.GC_ALLOC_PINNED_OBJECT_HEAP; + GC_ALLOC_FLAGS flags = GC_ALLOC_FLAGS.GC_ALLOC_ZEROING_OPTIONAL; + if (pinned) + flags |= GC_ALLOC_FLAGS.GC_ALLOC_PINNED_OBJECT_HEAP; - return Unsafe.As(AllocateNewArray(RuntimeTypeHandle.ToIntPtr(typeof(T[]).TypeHandle), length, flags)); - } + return Unsafe.As(AllocateNewArray(RuntimeTypeHandle.ToIntPtr(typeof(T[]).TypeHandle), length, flags)); } /// diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 53deac240305c9..dfbcc9e87d0f69 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -3924,6 +3924,7 @@ class Compiler CORINFO_RESOLVED_TOKEN* pResolvedToken, bool readonlyCall, bool tailCall, + bool callvirt, CORINFO_RESOLVED_TOKEN* pContstrainedResolvedToken, CORINFO_THIS_TRANSFORM constraintCallThisTransform, NamedIntrinsic* pIntrinsicName, diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 91e32c182bd772..2db73c09d38fc5 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -225,7 +225,8 @@ var_types Compiler::impImportCall(OPCODE opcode, const bool isTailCall = canTailCall && (tailCallFlags != 0); call = impIntrinsic(newobjThis, clsHnd, methHnd, sig, mflags, pResolvedToken, isReadonlyCall, isTailCall, - pConstrainedResolvedToken, callInfo->thisTransform, &ni, &isSpecialIntrinsic); + opcode == CEE_CALLVIRT, pConstrainedResolvedToken, callInfo->thisTransform, &ni, + &isSpecialIntrinsic); if (compDonotInline()) { @@ -2288,6 +2289,7 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, CORINFO_RESOLVED_TOKEN* pResolvedToken, bool readonlyCall, bool tailCall, + bool callvirt, CORINFO_RESOLVED_TOKEN* pConstrainedResolvedToken, CORINFO_THIS_TRANSFORM constraintCallThisTransform, NamedIntrinsic* pIntrinsicName, @@ -2574,7 +2576,6 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, case NI_System_Runtime_CompilerServices_RuntimeHelpers_IsKnownConstant: // We need these to be able to fold "typeof(...) == typeof(...)" - case NI_System_RuntimeTypeHandle_ToIntPtr: case NI_System_Type_GetTypeFromHandle: case NI_System_Type_op_Equality: case NI_System_Type_op_Inequality: @@ -2599,6 +2600,9 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, case NI_System_BitConverter_SingleToInt32Bits: case NI_System_Buffers_Binary_BinaryPrimitives_ReverseEndianness: case NI_System_Type_GetEnumUnderlyingType: + case NI_System_Type_get_TypeHandle: + case NI_System_RuntimeType_get_TypeHandle: + case NI_System_RuntimeTypeHandle_ToIntPtr: // Most atomics are compiled to single instructions case NI_System_Threading_Interlocked_And: @@ -2955,8 +2959,8 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, case NI_System_RuntimeTypeHandle_ToIntPtr: { GenTree* op1 = impStackTop(0).val; - if (op1->gtOper == GT_CALL && (op1->AsCall()->gtCallType == CT_HELPER) && - gtIsTypeHandleToRuntimeTypeHandleHelper(op1->AsCall())) + + if (op1->IsHelperCall() && gtIsTypeHandleToRuntimeTypeHandleHelper(op1->AsCall())) { // Old tree // Helper-RuntimeTypeHandle -> TreeToGetNativeTypeHandle @@ -2974,7 +2978,28 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, op1 = op1->AsCall()->gtArgs.GetArgByIndex(0)->GetNode(); retNode = op1; } - // Call the regular function. + else if (op1->OperIs(GT_CALL, GT_RET_EXPR)) + { + // Skip roundtrip "handle -> RuntimeType -> handle" for + // RuntimeTypeHandle.ToIntPtr(typeof(T).TypeHandle) + GenTreeCall* call = op1->IsCall() ? op1->AsCall() : op1->AsRetExpr()->gtInlineCandidate; + if (lookupNamedIntrinsic(call->gtCallMethHnd) == NI_System_RuntimeType_get_TypeHandle) + { + // Check that the arg is CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE helper call + GenTree* arg = call->gtArgs.GetArgByIndex(0)->GetNode(); + if (arg->IsHelperCall() && gtIsTypeHandleToRuntimeTypeHelper(arg->AsCall())) + { + impPopStack(); + if (op1->OperIs(GT_RET_EXPR)) + { + // Bash the RET_EXPR's call to no-op since it's unused now + op1->AsRetExpr()->gtInlineCandidate->gtBashToNOP(); + } + // Skip roundtrip and return the type handle directly + retNode = arg->AsCall()->gtArgs.GetArgByIndex(0)->GetNode(); + } + } + } break; } @@ -3074,6 +3099,30 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, break; } + case NI_System_Type_get_TypeHandle: + { + // We can only expand this on NativeAOT where RuntimeTypeHandle looks like this: + // + // struct RuntimeTypeHandle { IntPtr _value; } + // + GenTree* op1 = impStackTop(0).val; + if (IsTargetAbi(CORINFO_NATIVEAOT_ABI) && op1->IsHelperCall() && + gtIsTypeHandleToRuntimeTypeHelper(op1->AsCall()) && callvirt) + { + assert(info.compCompHnd->getClassNumInstanceFields(sig->retTypeClass) == 1); + + unsigned structLcl = lvaGrabTemp(true DEBUGARG("RuntimeTypeHandle")); + lvaSetStruct(structLcl, sig->retTypeClass, false); + GenTree* realHandle = op1->AsCall()->gtArgs.GetUserArgByIndex(0)->GetNode(); + GenTreeLclFld* handleFld = gtNewLclFldNode(structLcl, realHandle->TypeGet(), 0); + GenTree* asgHandleFld = gtNewAssignNode(handleFld, realHandle); + impAppendTree(asgHandleFld, CHECK_SPILL_NONE, impCurStmtDI); + retNode = impCreateLocalNode(structLcl DEBUGARG(0)); + impPopStack(); + } + break; + } + case NI_System_Type_get_IsEnum: case NI_System_Type_get_IsValueType: case NI_System_Type_get_IsByRefLike: @@ -8122,6 +8171,10 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method) { result = NI_System_Type_get_IsEnum; } + if (strcmp(methodName, "get_TypeHandle") == 0) + { + result = NI_System_RuntimeType_get_TypeHandle; + } } else if (strcmp(className, "RuntimeTypeHandle") == 0) { @@ -8219,6 +8272,10 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method) { result = NI_System_Type_op_Inequality; } + else if (strcmp(methodName, "get_TypeHandle") == 0) + { + result = NI_System_Type_get_TypeHandle; + } } break; } diff --git a/src/coreclr/jit/namedintrinsiclist.h b/src/coreclr/jit/namedintrinsiclist.h index 1072528820b319..50fb3e3ea214bc 100644 --- a/src/coreclr/jit/namedintrinsiclist.h +++ b/src/coreclr/jit/namedintrinsiclist.h @@ -66,6 +66,7 @@ enum NamedIntrinsic : unsigned short NI_System_Type_GetEnumUnderlyingType, NI_System_Type_get_IsValueType, NI_System_Type_get_IsByRefLike, + NI_System_Type_get_TypeHandle, NI_System_Type_IsAssignableFrom, NI_System_Type_IsAssignableTo, NI_System_Type_op_Equality, @@ -78,6 +79,7 @@ enum NamedIntrinsic : unsigned short NI_System_Object_MemberwiseClone, NI_System_Object_GetType, NI_System_RuntimeTypeHandle_ToIntPtr, + NI_System_RuntimeType_get_TypeHandle, NI_System_StubHelpers_GetStubContext, NI_System_StubHelpers_NextCallReturnAddress, diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 1c4d2091691167..ae9d45fd28e2f3 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -11851,11 +11851,25 @@ bool CEEInfo::getObjectContent(CORINFO_OBJECT_HANDLE handle, uint8_t* buffer, in _ASSERTE(objRef != NULL); // TODO: support types containing GC pointers - if (!objRef->GetMethodTable()->ContainsPointers() && bufferSize + valueOffset <= (int)objRef->GetSize()) + if (bufferSize + valueOffset <= (int)objRef->GetSize()) { Object* obj = OBJECTREFToObject(objRef); - memcpy(buffer, (uint8_t*)obj + valueOffset, bufferSize); - result = true; + PTR_MethodTable type = obj->GetMethodTable(); + if (type->ContainsPointers()) + { + // RuntimeType has a gc field (object m_keepAlive), but if the object is in a frozen segment + // it means that field is always nullptr so we can read any part of the object: + if (type == g_pRuntimeTypeClass && GCHeapUtilities::GetGCHeap()->IsInFrozenSegment(obj)) + { + memcpy(buffer, (uint8_t*)obj + valueOffset, bufferSize); + result = true; + } + } + else + { + memcpy(buffer, (uint8_t*)obj + valueOffset, bufferSize); + result = true; + } } EE_TO_JIT_TRANSITION(); diff --git a/src/libraries/System.Private.CoreLib/src/System/RuntimeType.cs b/src/libraries/System.Private.CoreLib/src/System/RuntimeType.cs index 176a1f75b14763..943b00ad215a24 100644 --- a/src/libraries/System.Private.CoreLib/src/System/RuntimeType.cs +++ b/src/libraries/System.Private.CoreLib/src/System/RuntimeType.cs @@ -25,7 +25,12 @@ internal sealed partial class RuntimeType : TypeInfo, ICloneable public override int MetadataToken => RuntimeTypeHandle.GetToken(this); public override Module Module => GetRuntimeModule(); public override Type? ReflectedType => DeclaringType; - public override RuntimeTypeHandle TypeHandle => new RuntimeTypeHandle(this); + public override RuntimeTypeHandle TypeHandle + { + [Intrinsic] // to avoid round-trip "handle -> RuntimeType -> handle" in JIT + get => new RuntimeTypeHandle(this); + } + public override Type UnderlyingSystemType => this; public object Clone() => this; diff --git a/src/libraries/System.Private.CoreLib/src/System/Type.cs b/src/libraries/System.Private.CoreLib/src/System/Type.cs index 28fd7905e57727..87b03dcfcbdff7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Type.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Type.cs @@ -424,7 +424,12 @@ private protected static ArgumentException CreateGetMemberWithSameMetadataDefini | DynamicallyAccessedMemberTypes.PublicNestedTypes)] public virtual MemberInfo[] GetDefaultMembers() => throw NotImplemented.ByDesign; - public virtual RuntimeTypeHandle TypeHandle => throw new NotSupportedException(); + public virtual RuntimeTypeHandle TypeHandle + { + [Intrinsic] + get => throw new NotSupportedException(); + } + public static RuntimeTypeHandle GetTypeHandle(object o) { ArgumentNullException.ThrowIfNull(o);