From 16b28c704108c691c54a73ad845069aac2e3e282 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Fri, 11 Nov 2022 05:50:14 +0100 Subject: [PATCH] Make Type.IsEnum and Type.GetEnumUnderlyingType intrinsics (#71685) * Make Type.GetTypeCode an intrinsic Makes Type.GetTypeCode a JIT intrinsic for primitive types, enums and strings. Makes Type.IsEnum use intrinsics instead of IsSubclassOf Makes Enum.GetUnderlyingType use Type.GetTypeCode Moves the legacy FCall to InternalGetUnderlyingTypeImpl, so that the non-intrinsic GetTypeCode can use it. Introduces JIT tests checking the generated codegen. * Change IsEnum to an intrinsic * Fallback to the FCall for nint/nuint enums * Fix compilation * Add missing Intrinsic * Add typeof support to IsKnownConstant * Use gtIsTypeHandleToRuntimeTypeHelper * Add __reftype tests * Make more places use gtIsTypeof * Update EnumTests.cs * Add impTypeGetTypeCode to the header * Update EnumGetUnderlyingTypeEnums.il * Make the IsActualEnum helper an intrinsic * Remove GetTypeCode intrinsics * Create a new JIT-EE api, add GetEnumUnderlyingType back * Optimize GetTypeCode with IsKnownConstant * Change the code to make the inliner happy * Handle all types in GetTypeCode * Check for custom types * Fix build, do suggested changes Co-authored-by: Andy Ayers Co-authored-by: Jan Kotas --- .../src/System/RuntimeType.CoreCLR.cs | 1 + src/coreclr/inc/corinfo.h | 14 +- src/coreclr/inc/icorjitinfoimpl_generated.h | 4 + src/coreclr/inc/jiteeversionguid.h | 10 +- src/coreclr/jit/ICorJitInfo_names_generated.h | 1 + .../jit/ICorJitInfo_wrapper_generated.hpp | 10 + src/coreclr/jit/compiler.h | 2 + src/coreclr/jit/fgbasic.cpp | 2 + src/coreclr/jit/gentree.cpp | 36 +++ src/coreclr/jit/importer.cpp | 52 ++--- src/coreclr/jit/importercalls.cpp | 77 +++++-- src/coreclr/jit/morph.cpp | 2 +- src/coreclr/jit/namedintrinsiclist.h | 2 + .../tools/Common/JitInterface/CorInfoImpl.cs | 30 +++ .../JitInterface/CorInfoImpl_generated.cs | 212 ++++++++++-------- .../ThunkGenerator/ThunkInput.txt | 1 + .../aot/jitinterface/jitinterface_generated.h | 11 + .../tools/superpmi/superpmi-shared/lwmlist.h | 1 + .../superpmi-shared/methodcontext.cpp | 35 +++ .../superpmi/superpmi-shared/methodcontext.h | 5 + .../superpmi-shim-collector/icorjitinfo.cpp | 18 ++ .../icorjitinfo_generated.cpp | 8 + .../icorjitinfo_generated.cpp | 7 + .../tools/superpmi/superpmi/icorjitinfo.cpp | 11 + src/coreclr/vm/jitinterface.cpp | 51 +++++ .../Collections/Generic/EqualityComparer.cs | 2 +- .../CompilerServices/RuntimeHelpers.cs | 3 + .../src/System/RuntimeType.cs | 61 +---- .../System.Private.CoreLib/src/System/Type.cs | 51 ++++- .../System.Runtime/tests/System/EnumTests.cs | 6 +- .../TypeIntrinsics.GetEnumUnderlyingType.cs | 111 +++++++++ .../TypeIntrinsics.IsAssignableFrom.cs | 16 +- .../TypeIntrinsics.IsAssignableTo.cs | 16 +- src/tests/JIT/Intrinsics/TypeIntrinsics.cs | 43 +++- .../JIT/Intrinsics/TypeIntrinsicsEnums.il | 42 ++++ .../JIT/Intrinsics/TypeIntrinsicsEnums.ilproj | 11 + .../JIT/Intrinsics/TypeIntrinsics_r.csproj | 4 + .../JIT/Intrinsics/TypeIntrinsics_ro.csproj | 4 + .../opt/IsKnownConstant/TypeGetTypeCode.cs | 94 ++++++++ .../IsKnownConstant/TypeGetTypeCode.csproj | 12 + .../IsKnownConstant/TypeGetTypeCodeEnums.il | 42 ++++ .../TypeGetTypeCodeEnums.ilproj | 11 + 42 files changed, 903 insertions(+), 229 deletions(-) create mode 100644 src/tests/JIT/Intrinsics/TypeIntrinsics.GetEnumUnderlyingType.cs create mode 100644 src/tests/JIT/Intrinsics/TypeIntrinsicsEnums.il create mode 100644 src/tests/JIT/Intrinsics/TypeIntrinsicsEnums.ilproj create mode 100644 src/tests/JIT/opt/IsKnownConstant/TypeGetTypeCode.cs create mode 100644 src/tests/JIT/opt/IsKnownConstant/TypeGetTypeCode.csproj create mode 100644 src/tests/JIT/opt/IsKnownConstant/TypeGetTypeCodeEnums.il create mode 100644 src/tests/JIT/opt/IsKnownConstant/TypeGetTypeCodeEnums.ilproj diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs index 60416d4dbb724..fdfc88b419e4a 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs @@ -3397,6 +3397,7 @@ public override unsafe bool IsEnum // This returns true for actual enum types only. internal unsafe bool IsActualEnum { + [Intrinsic] get { TypeHandle th = GetNativeTypeHandle(); diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index 4958de749672e..b4faaf34861d1 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -2270,7 +2270,7 @@ class ICorStaticInfo //------------------------------------------------------------------------------ // printObjectDescription: Prints a (possibly truncated) textual UTF8 representation of the given - // object to a preallocated buffer. It's intended to be used only for debug/diagnostic + // object to a preallocated buffer. It's intended to be used only for debug/diagnostic // purposes such as JitDisasm. The buffer is null-terminated (even if truncated). // // Arguments: @@ -2635,6 +2635,16 @@ class ICorStaticInfo CORINFO_CLASS_HANDLE cls2 ) = 0; + // Returns TypeCompareState::Must if cls is known to be an enum. + // For enums with known exact type returns the underlying + // type in underlyingType when the provided pointer is + // non-NULL. + // Returns TypeCompareState::May when a runtime check is required. + virtual TypeCompareState isEnum( + CORINFO_CLASS_HANDLE cls, + CORINFO_CLASS_HANDLE* underlyingType + ) = 0; + // Given a class handle, returns the Parent type. // For COMObjectType, it returns Class Handle of System.Object. // Returns 0 if System.Object is passed in. @@ -2849,7 +2859,7 @@ class ICorStaticInfo CORINFO_CLASS_HANDLE *vcTypeRet /* OUT */ ) = 0; - // Obtains a list of exact classes for a given base type. Returns 0 if the number of + // Obtains a list of exact classes for a given base type. Returns 0 if the number of // the exact classes is greater than maxExactClasses or if more types might be loaded // in future. virtual int getExactClasses( diff --git a/src/coreclr/inc/icorjitinfoimpl_generated.h b/src/coreclr/inc/icorjitinfoimpl_generated.h index 44c98ae5397f5..dc05dced40a16 100644 --- a/src/coreclr/inc/icorjitinfoimpl_generated.h +++ b/src/coreclr/inc/icorjitinfoimpl_generated.h @@ -352,6 +352,10 @@ bool isMoreSpecificType( CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2) override; +TypeCompareState isEnum( + CORINFO_CLASS_HANDLE cls, + CORINFO_CLASS_HANDLE* underlyingType) override; + CORINFO_CLASS_HANDLE getParentType( CORINFO_CLASS_HANDLE cls) override; diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h index 2f1bb07461e18..601ba406d8334 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 = { /* 77b6df16-d27f-4118-9dfd-d8073ff20fb6 */ - 0x77b6df16, - 0xd27f, - 0x4118, - {0x9d, 0xfd, 0xd8, 0x7, 0x3f, 0xf2, 0xf, 0xb6} +constexpr GUID JITEEVersionIdentifier = { /* e452af1d-0a1a-44a8-a5b3-ef6074b8ab4a */ + 0xe452af1d, + 0x0a1a, + 0x44a8, + {0xa5, 0xb3, 0xef, 0x60, 0x74, 0xb8, 0xab, 0x4a} }; ////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/coreclr/jit/ICorJitInfo_names_generated.h b/src/coreclr/jit/ICorJitInfo_names_generated.h index 8c43eb4823aef..ed00cc191122b 100644 --- a/src/coreclr/jit/ICorJitInfo_names_generated.h +++ b/src/coreclr/jit/ICorJitInfo_names_generated.h @@ -88,6 +88,7 @@ DEF_CLR_API(compareTypesForCast) DEF_CLR_API(compareTypesForEquality) DEF_CLR_API(mergeClasses) DEF_CLR_API(isMoreSpecificType) +DEF_CLR_API(isEnum) DEF_CLR_API(getParentType) DEF_CLR_API(getChildType) DEF_CLR_API(satisfiesClassConstraints) diff --git a/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp b/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp index 057f5ba417759..8cedeccd48769 100644 --- a/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp +++ b/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp @@ -830,6 +830,16 @@ bool WrapICorJitInfo::isMoreSpecificType( return temp; } +TypeCompareState WrapICorJitInfo::isEnum( + CORINFO_CLASS_HANDLE cls, + CORINFO_CLASS_HANDLE* underlyingType) +{ + API_ENTER(isEnum); + TypeCompareState temp = wrapHnd->isEnum(cls, underlyingType); + API_LEAVE(isEnum); + return temp; +} + CORINFO_CLASS_HANDLE WrapICorJitInfo::getParentType( CORINFO_CLASS_HANDLE cls) { diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 068dd098cc4a3..a1b860ed99c2e 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -2878,6 +2878,8 @@ class Compiler CORINFO_CLASS_HANDLE gtGetHelperArgClassHandle(GenTree* array); // Get the class handle for a field CORINFO_CLASS_HANDLE gtGetFieldClassHandle(CORINFO_FIELD_HANDLE fieldHnd, bool* pIsExact, bool* pIsNonNull); + // Check if this tree is a typeof() + bool gtIsTypeof(GenTree* tree, CORINFO_CLASS_HANDLE* handle = nullptr); GenTree* gtCallGetDefinedRetBufLclAddr(GenTreeCall* call); diff --git a/src/coreclr/jit/fgbasic.cpp b/src/coreclr/jit/fgbasic.cpp index 29f0afa67eac5..ea7f8fccd9b0c 100644 --- a/src/coreclr/jit/fgbasic.cpp +++ b/src/coreclr/jit/fgbasic.cpp @@ -1163,6 +1163,8 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed break; // These are foldable if the first argument is a constant + case NI_System_Type_get_IsEnum: + case NI_System_Type_GetEnumUnderlyingType: case NI_System_Type_get_IsValueType: case NI_System_Type_get_IsByRefLike: case NI_System_Type_GetTypeFromHandle: diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 186fcabe5fb51..24081df5c38ae 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -18335,6 +18335,42 @@ CORINFO_CLASS_HANDLE Compiler::gtGetFieldClassHandle(CORINFO_FIELD_HANDLE fieldH return fieldClass; } +//------------------------------------------------------------------------ +// gtIsTypeof: Checks if the tree is a typeof() +// +// Arguments: +// tree - the tree that is checked +// handle - (optional, default nullptr) - if non-null is set to the type +// +// Return Value: +// Is the tree typeof() +// +bool Compiler::gtIsTypeof(GenTree* tree, CORINFO_CLASS_HANDLE* handle) +{ + if (tree->IsCall()) + { + GenTreeCall* call = tree->AsCall(); + if (gtIsTypeHandleToRuntimeTypeHelper(call)) + { + assert(call->gtArgs.CountArgs() == 1); + CORINFO_CLASS_HANDLE hClass = gtGetHelperArgClassHandle(call->gtArgs.GetArgByIndex(0)->GetEarlyNode()); + if (hClass != NO_CLASS_HANDLE) + { + if (handle != nullptr) + { + *handle = hClass; + } + return true; + } + } + } + if (handle != nullptr) + { + *handle = NO_CLASS_HANDLE; + } + return false; +} + //------------------------------------------------------------------------ // gtCallGetDefinedRetBufLclAddr: // Get the tree corresponding to the address of the retbuf that this call defines. diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 65fb915f783ce..19e2c842393b5 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -2932,37 +2932,24 @@ GenTree* Compiler::impTypeIsAssignable(GenTree* typeTo, GenTree* typeFrom) // // to true/false - if (typeTo->IsCall() && typeFrom->IsCall()) + // make sure both arguments are `typeof()` + CORINFO_CLASS_HANDLE hClassTo = NO_CLASS_HANDLE; + CORINFO_CLASS_HANDLE hClassFrom = NO_CLASS_HANDLE; + if (gtIsTypeof(typeTo, &hClassTo) && gtIsTypeof(typeFrom, &hClassFrom)) { - // make sure both arguments are `typeof()` - CORINFO_METHOD_HANDLE hTypeof = eeFindHelper(CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE); - if ((typeTo->AsCall()->gtCallMethHnd == hTypeof) && (typeFrom->AsCall()->gtCallMethHnd == hTypeof)) + TypeCompareState castResult = info.compCompHnd->compareTypesForCast(hClassFrom, hClassTo); + if (castResult == TypeCompareState::May) { - assert((typeTo->AsCall()->gtArgs.CountArgs() == 1) && (typeFrom->AsCall()->gtArgs.CountArgs() == 1)); - CORINFO_CLASS_HANDLE hClassTo = - gtGetHelperArgClassHandle(typeTo->AsCall()->gtArgs.GetArgByIndex(0)->GetEarlyNode()); - CORINFO_CLASS_HANDLE hClassFrom = - gtGetHelperArgClassHandle(typeFrom->AsCall()->gtArgs.GetArgByIndex(0)->GetEarlyNode()); - - if (hClassTo == NO_CLASS_HANDLE || hClassFrom == NO_CLASS_HANDLE) - { - return nullptr; - } - - TypeCompareState castResult = info.compCompHnd->compareTypesForCast(hClassFrom, hClassTo); - if (castResult == TypeCompareState::May) - { - // requires runtime check - // e.g. __Canon, COMObjects, Nullable - return nullptr; - } + // requires runtime check + // e.g. __Canon, COMObjects, Nullable + return nullptr; + } - GenTreeIntCon* retNode = gtNewIconNode((castResult == TypeCompareState::Must) ? 1 : 0); - impPopStack(); // drop both CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE calls - impPopStack(); + GenTreeIntCon* retNode = gtNewIconNode((castResult == TypeCompareState::Must) ? 1 : 0); + impPopStack(); // drop both CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE calls + impPopStack(); - return retNode; - } + return retNode; } return nullptr; @@ -13391,15 +13378,10 @@ void Compiler::impInlineRecordArgInfo(InlineInfo* pInlineInfo, return; } } - else + else if (gtIsTypeof(curArgVal)) { - if (curArgVal->IsHelperCall() && gtIsTypeHandleToRuntimeTypeHelper(curArgVal->AsCall()) && - (gtGetHelperArgClassHandle(curArgVal->AsCall()->gtArgs.GetArgByIndex(0)->GetEarlyNode()) != - NO_CLASS_HANDLE)) - { - inlCurArgInfo->argIsInvariant = true; - inlCurArgInfo->argHasSideEff = false; - } + inlCurArgInfo->argIsInvariant = true; + inlCurArgInfo->argHasSideEff = false; } bool isExact = false; diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index a5aa32c1cd721..b2cbc6965f578 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -2587,12 +2587,12 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, case NI_System_Runtime_CompilerServices_RuntimeHelpers_IsKnownConstant: { GenTree* op1 = impPopStack().val; - if (op1->OperIsConst()) + if (op1->OperIsConst() || gtIsTypeof(op1)) { // op1 is a known constant, replace with 'true'. retNode = gtNewIconNode(1); JITDUMP("\nExpanding RuntimeHelpers.IsKnownConstant to true early\n"); - // We can also consider FTN_ADDR and typeof(T) here + // We can also consider FTN_ADDR here } else { @@ -2854,6 +2854,7 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, break; } + case NI_System_Type_get_IsEnum: case NI_System_Type_get_IsValueType: case NI_System_Type_get_IsByRefLike: { @@ -2865,35 +2866,56 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, // to `true` or `false` // e.g., `typeof(int).IsValueType` => `true` // e.g., `typeof(Span).IsByRefLike` => `true` - if (impStackTop().val->IsCall()) + CORINFO_CLASS_HANDLE hClass = NO_CLASS_HANDLE; + if (gtIsTypeof(impStackTop().val, &hClass)) { - GenTreeCall* call = impStackTop().val->AsCall(); - if (call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE)) + switch (ni) { - assert(call->gtArgs.CountArgs() == 1); - CORINFO_CLASS_HANDLE hClass = - gtGetHelperArgClassHandle(call->gtArgs.GetArgByIndex(0)->GetEarlyNode()); - if (hClass != NO_CLASS_HANDLE) + case NI_System_Type_get_IsEnum: { - switch (ni) + TypeCompareState state = info.compCompHnd->isEnum(hClass, nullptr); + if (state == TypeCompareState::May) { - case NI_System_Type_get_IsValueType: - retNode = gtNewIconNode(eeIsValueClass(hClass) ? 1 : 0); - break; - case NI_System_Type_get_IsByRefLike: - retNode = gtNewIconNode( - (info.compCompHnd->getClassAttribs(hClass) & CORINFO_FLG_BYREF_LIKE) ? 1 : 0); - break; - default: - NO_WAY("Intrinsic not supported in this path."); + retNode = nullptr; + break; } - impPopStack(); // drop CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE call + retNode = gtNewIconNode(state == TypeCompareState::Must ? 1 : 0); + break; } + case NI_System_Type_get_IsValueType: + retNode = gtNewIconNode(eeIsValueClass(hClass) ? 1 : 0); + break; + case NI_System_Type_get_IsByRefLike: + retNode = gtNewIconNode( + (info.compCompHnd->getClassAttribs(hClass) & CORINFO_FLG_BYREF_LIKE) ? 1 : 0); + break; + default: + NO_WAY("Intrinsic not supported in this path."); + } + if (retNode != nullptr) + { + impPopStack(); // drop CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE call } } break; } + case NI_System_Type_GetEnumUnderlyingType: + { + GenTree* type = impStackTop().val; + CORINFO_CLASS_HANDLE hClassEnum = NO_CLASS_HANDLE; + CORINFO_CLASS_HANDLE hClassUnderlying = NO_CLASS_HANDLE; + if (gtIsTypeof(type, &hClassEnum) && (hClassEnum != NO_CLASS_HANDLE) && + (info.compCompHnd->isEnum(hClassEnum, &hClassUnderlying) == TypeCompareState::Must) && + (hClassUnderlying != NO_CLASS_HANDLE)) + { + GenTree* handle = gtNewIconEmbClsHndNode(hClassUnderlying); + retNode = gtNewHelperCallNode(CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE, TYP_REF, handle); + impPopStack(); + } + break; + } + case NI_System_Threading_Thread_get_ManagedThreadId: { if (impStackTop().val->OperIs(GT_RET_EXPR)) @@ -7140,6 +7162,13 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method) result = NI_System_RuntimeTypeHandle_GetValueInternal; } } + else if (strcmp(className, "RuntimeType") == 0) + { + if (strcmp(methodName, "get_IsActualEnum") == 0) + { + result = NI_System_Type_get_IsEnum; + } + } else if (strcmp(className, "Type") == 0) { if (strcmp(methodName, "get_IsValueType") == 0) @@ -7170,6 +7199,14 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method) { result = NI_System_Type_GetTypeFromHandle; } + else if (strcmp(methodName, "get_IsEnum") == 0) + { + result = NI_System_Type_get_IsEnum; + } + else if (strcmp(methodName, "GetEnumUnderlyingType") == 0) + { + result = NI_System_Type_GetEnumUnderlyingType; + } } else if (strcmp(className, "String") == 0) { diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 2842b3b3e7dbd..16b28efe45a01 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -11020,7 +11020,7 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac, bool* optA assert(!optValnumCSE_phase); JITDUMP("\nExpanding RuntimeHelpers.IsKnownConstant to "); - if (op1->OperIsConst()) + if (op1->OperIsConst() || gtIsTypeof(op1)) { // We're lucky to catch a constant here while importer was not JITDUMP("true\n"); diff --git a/src/coreclr/jit/namedintrinsiclist.h b/src/coreclr/jit/namedintrinsiclist.h index e33b1f391f6d4..37af303c0ab68 100644 --- a/src/coreclr/jit/namedintrinsiclist.h +++ b/src/coreclr/jit/namedintrinsiclist.h @@ -60,6 +60,8 @@ enum NamedIntrinsic : unsigned short NI_System_GC_KeepAlive, NI_System_Threading_Thread_get_CurrentThread, NI_System_Threading_Thread_get_ManagedThreadId, + NI_System_Type_get_IsEnum, + NI_System_Type_GetEnumUnderlyingType, NI_System_Type_get_IsValueType, NI_System_Type_get_IsByRefLike, NI_System_Type_IsAssignableFrom, diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 62beee1b926e4..7b1ce731f2563 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -2753,6 +2753,36 @@ private bool isMoreSpecificType(CORINFO_CLASS_STRUCT_* cls1, CORINFO_CLASS_STRUC return merged == type1; } + private TypeCompareState isEnum(CORINFO_CLASS_STRUCT_* cls, CORINFO_CLASS_STRUCT_** underlyingType) + { + Debug.Assert(cls != null); + + if (underlyingType != null) + { + *underlyingType = null; + } + + TypeDesc type = HandleToObject(cls); + + if (type.IsGenericParameter) + { + return TypeCompareState.May; + } + + if (type.IsEnum) + { + if (underlyingType != null) + { + *underlyingType = ObjectToHandle(type.UnderlyingType); + } + return TypeCompareState.Must; + } + else + { + return TypeCompareState.MustNot; + } + } + private CORINFO_CLASS_STRUCT_* getParentType(CORINFO_CLASS_STRUCT_* cls) { throw new NotImplementedException("getParentType"); } diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs index 4b830673d4a8c..40218af44e79a 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs @@ -1255,6 +1255,21 @@ private static byte _isMoreSpecificType(IntPtr thisHandle, IntPtr* ppException, } } + [UnmanagedCallersOnly] + private static TypeCompareState _isEnum(IntPtr thisHandle, IntPtr* ppException, CORINFO_CLASS_STRUCT_* cls, CORINFO_CLASS_STRUCT_** underlyingType) + { + var _this = GetThis(thisHandle); + try + { + return _this.isEnum(cls, underlyingType); + } + catch (Exception ex) + { + *ppException = _this.AllocException(ex); + return default; + } + } + [UnmanagedCallersOnly] private static CORINFO_CLASS_STRUCT_* _getParentType(IntPtr thisHandle, IntPtr* ppException, CORINFO_CLASS_STRUCT_* cls) { @@ -2685,7 +2700,7 @@ private static uint _getJitFlags(IntPtr thisHandle, IntPtr* ppException, CORJIT_ private static IntPtr GetUnmanagedCallbacks() { - void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 181); + void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 182); callbacks[0] = (delegate* unmanaged)&_isIntrinsic; callbacks[1] = (delegate* unmanaged)&_getMethodAttribs; @@ -2771,103 +2786,104 @@ private static IntPtr GetUnmanagedCallbacks() callbacks[81] = (delegate* unmanaged)&_compareTypesForEquality; callbacks[82] = (delegate* unmanaged)&_mergeClasses; callbacks[83] = (delegate* unmanaged)&_isMoreSpecificType; - callbacks[84] = (delegate* unmanaged)&_getParentType; - callbacks[85] = (delegate* unmanaged)&_getChildType; - callbacks[86] = (delegate* unmanaged)&_satisfiesClassConstraints; - callbacks[87] = (delegate* unmanaged)&_isSDArray; - callbacks[88] = (delegate* unmanaged)&_getArrayRank; - callbacks[89] = (delegate* unmanaged)&_getArrayIntrinsicID; - callbacks[90] = (delegate* unmanaged)&_getArrayInitializationData; - callbacks[91] = (delegate* unmanaged)&_canAccessClass; - callbacks[92] = (delegate* unmanaged)&_getFieldName; - callbacks[93] = (delegate* unmanaged)&_getFieldClass; - callbacks[94] = (delegate* unmanaged)&_getFieldType; - callbacks[95] = (delegate* unmanaged)&_getFieldOffset; - callbacks[96] = (delegate* unmanaged)&_getFieldInfo; - callbacks[97] = (delegate* unmanaged)&_isFieldStatic; - callbacks[98] = (delegate* unmanaged)&_getArrayOrStringLength; - callbacks[99] = (delegate* unmanaged)&_getBoundaries; - callbacks[100] = (delegate* unmanaged)&_setBoundaries; - callbacks[101] = (delegate* unmanaged)&_getVars; - callbacks[102] = (delegate* unmanaged)&_setVars; - callbacks[103] = (delegate* unmanaged)&_reportRichMappings; - callbacks[104] = (delegate* unmanaged)&_allocateArray; - callbacks[105] = (delegate* unmanaged)&_freeArray; - callbacks[106] = (delegate* unmanaged)&_getArgNext; - callbacks[107] = (delegate* unmanaged)&_getArgType; - callbacks[108] = (delegate* unmanaged)&_getExactClasses; - callbacks[109] = (delegate* unmanaged)&_getArgClass; - callbacks[110] = (delegate* unmanaged)&_getHFAType; - callbacks[111] = (delegate* unmanaged)&_GetErrorHRESULT; - callbacks[112] = (delegate* unmanaged)&_GetErrorMessage; - callbacks[113] = (delegate* unmanaged)&_FilterException; - callbacks[114] = (delegate* unmanaged)&_ThrowExceptionForJitResult; - callbacks[115] = (delegate* unmanaged)&_ThrowExceptionForHelper; - callbacks[116] = (delegate* unmanaged)&_runWithErrorTrap; - callbacks[117] = (delegate* unmanaged)&_runWithSPMIErrorTrap; - callbacks[118] = (delegate* unmanaged)&_getEEInfo; - callbacks[119] = (delegate* unmanaged)&_getJitTimeLogFilename; - callbacks[120] = (delegate* unmanaged)&_getMethodDefFromMethod; - callbacks[121] = (delegate* unmanaged)&_getMethodName; - callbacks[122] = (delegate* unmanaged)&_getMethodNameFromMetadata; - callbacks[123] = (delegate* unmanaged)&_getMethodHash; - callbacks[124] = (delegate* unmanaged)&_findNameOfToken; - callbacks[125] = (delegate* unmanaged)&_getSystemVAmd64PassStructInRegisterDescriptor; - callbacks[126] = (delegate* unmanaged)&_getLoongArch64PassStructInRegisterFlags; - callbacks[127] = (delegate* unmanaged)&_getThreadTLSIndex; - callbacks[128] = (delegate* unmanaged)&_getInlinedCallFrameVptr; - callbacks[129] = (delegate* unmanaged)&_getAddrOfCaptureThreadGlobal; - callbacks[130] = (delegate* unmanaged)&_getHelperFtn; - callbacks[131] = (delegate* unmanaged)&_getFunctionEntryPoint; - callbacks[132] = (delegate* unmanaged)&_getFunctionFixedEntryPoint; - callbacks[133] = (delegate* unmanaged)&_getMethodSync; - callbacks[134] = (delegate* unmanaged)&_getLazyStringLiteralHelper; - callbacks[135] = (delegate* unmanaged)&_embedModuleHandle; - callbacks[136] = (delegate* unmanaged)&_embedClassHandle; - callbacks[137] = (delegate* unmanaged)&_embedMethodHandle; - callbacks[138] = (delegate* unmanaged)&_embedFieldHandle; - callbacks[139] = (delegate* unmanaged)&_embedGenericHandle; - callbacks[140] = (delegate* unmanaged)&_getLocationOfThisType; - callbacks[141] = (delegate* unmanaged)&_getAddressOfPInvokeTarget; - callbacks[142] = (delegate* unmanaged)&_GetCookieForPInvokeCalliSig; - callbacks[143] = (delegate* unmanaged)&_canGetCookieForPInvokeCalliSig; - callbacks[144] = (delegate* unmanaged)&_getJustMyCodeHandle; - callbacks[145] = (delegate* unmanaged)&_GetProfilingHandle; - callbacks[146] = (delegate* unmanaged)&_getCallInfo; - callbacks[147] = (delegate* unmanaged)&_canAccessFamily; - callbacks[148] = (delegate* unmanaged)&_isRIDClassDomainID; - callbacks[149] = (delegate* unmanaged)&_getClassDomainID; - callbacks[150] = (delegate* unmanaged)&_getFieldAddress; - callbacks[151] = (delegate* unmanaged)&_getReadonlyStaticFieldValue; - callbacks[152] = (delegate* unmanaged)&_getStaticFieldCurrentClass; - callbacks[153] = (delegate* unmanaged)&_getVarArgsHandle; - callbacks[154] = (delegate* unmanaged)&_canGetVarArgsHandle; - callbacks[155] = (delegate* unmanaged)&_constructStringLiteral; - callbacks[156] = (delegate* unmanaged)&_emptyStringLiteral; - callbacks[157] = (delegate* unmanaged)&_getFieldThreadLocalStoreID; - callbacks[158] = (delegate* unmanaged)&_addActiveDependency; - callbacks[159] = (delegate* unmanaged)&_GetDelegateCtor; - callbacks[160] = (delegate* unmanaged)&_MethodCompileComplete; - callbacks[161] = (delegate* unmanaged)&_getTailCallHelpers; - callbacks[162] = (delegate* unmanaged)&_convertPInvokeCalliToCall; - callbacks[163] = (delegate* unmanaged)&_notifyInstructionSetUsage; - callbacks[164] = (delegate* unmanaged)&_updateEntryPointForTailCall; - callbacks[165] = (delegate* unmanaged)&_allocMem; - callbacks[166] = (delegate* unmanaged)&_reserveUnwindInfo; - callbacks[167] = (delegate* unmanaged)&_allocUnwindInfo; - callbacks[168] = (delegate* unmanaged)&_allocGCInfo; - callbacks[169] = (delegate* unmanaged)&_setEHcount; - callbacks[170] = (delegate* unmanaged)&_setEHinfo; - callbacks[171] = (delegate* unmanaged)&_logMsg; - callbacks[172] = (delegate* unmanaged)&_doAssert; - callbacks[173] = (delegate* unmanaged)&_reportFatalError; - callbacks[174] = (delegate* unmanaged)&_getPgoInstrumentationResults; - callbacks[175] = (delegate* unmanaged)&_allocPgoInstrumentationBySchema; - callbacks[176] = (delegate* unmanaged)&_recordCallSite; - callbacks[177] = (delegate* unmanaged)&_recordRelocation; - callbacks[178] = (delegate* unmanaged)&_getRelocTypeHint; - callbacks[179] = (delegate* unmanaged)&_getExpectedTargetArchitecture; - callbacks[180] = (delegate* unmanaged)&_getJitFlags; + callbacks[84] = (delegate* unmanaged)&_isEnum; + callbacks[85] = (delegate* unmanaged)&_getParentType; + callbacks[86] = (delegate* unmanaged)&_getChildType; + callbacks[87] = (delegate* unmanaged)&_satisfiesClassConstraints; + callbacks[88] = (delegate* unmanaged)&_isSDArray; + callbacks[89] = (delegate* unmanaged)&_getArrayRank; + callbacks[90] = (delegate* unmanaged)&_getArrayIntrinsicID; + callbacks[91] = (delegate* unmanaged)&_getArrayInitializationData; + callbacks[92] = (delegate* unmanaged)&_canAccessClass; + callbacks[93] = (delegate* unmanaged)&_getFieldName; + callbacks[94] = (delegate* unmanaged)&_getFieldClass; + callbacks[95] = (delegate* unmanaged)&_getFieldType; + callbacks[96] = (delegate* unmanaged)&_getFieldOffset; + callbacks[97] = (delegate* unmanaged)&_getFieldInfo; + callbacks[98] = (delegate* unmanaged)&_isFieldStatic; + callbacks[99] = (delegate* unmanaged)&_getArrayOrStringLength; + callbacks[100] = (delegate* unmanaged)&_getBoundaries; + callbacks[101] = (delegate* unmanaged)&_setBoundaries; + callbacks[102] = (delegate* unmanaged)&_getVars; + callbacks[103] = (delegate* unmanaged)&_setVars; + callbacks[104] = (delegate* unmanaged)&_reportRichMappings; + callbacks[105] = (delegate* unmanaged)&_allocateArray; + callbacks[106] = (delegate* unmanaged)&_freeArray; + callbacks[107] = (delegate* unmanaged)&_getArgNext; + callbacks[108] = (delegate* unmanaged)&_getArgType; + callbacks[109] = (delegate* unmanaged)&_getExactClasses; + callbacks[110] = (delegate* unmanaged)&_getArgClass; + callbacks[111] = (delegate* unmanaged)&_getHFAType; + callbacks[112] = (delegate* unmanaged)&_GetErrorHRESULT; + callbacks[113] = (delegate* unmanaged)&_GetErrorMessage; + callbacks[114] = (delegate* unmanaged)&_FilterException; + callbacks[115] = (delegate* unmanaged)&_ThrowExceptionForJitResult; + callbacks[116] = (delegate* unmanaged)&_ThrowExceptionForHelper; + callbacks[117] = (delegate* unmanaged)&_runWithErrorTrap; + callbacks[118] = (delegate* unmanaged)&_runWithSPMIErrorTrap; + callbacks[119] = (delegate* unmanaged)&_getEEInfo; + callbacks[120] = (delegate* unmanaged)&_getJitTimeLogFilename; + callbacks[121] = (delegate* unmanaged)&_getMethodDefFromMethod; + callbacks[122] = (delegate* unmanaged)&_getMethodName; + callbacks[123] = (delegate* unmanaged)&_getMethodNameFromMetadata; + callbacks[124] = (delegate* unmanaged)&_getMethodHash; + callbacks[125] = (delegate* unmanaged)&_findNameOfToken; + callbacks[126] = (delegate* unmanaged)&_getSystemVAmd64PassStructInRegisterDescriptor; + callbacks[127] = (delegate* unmanaged)&_getLoongArch64PassStructInRegisterFlags; + callbacks[128] = (delegate* unmanaged)&_getThreadTLSIndex; + callbacks[129] = (delegate* unmanaged)&_getInlinedCallFrameVptr; + callbacks[130] = (delegate* unmanaged)&_getAddrOfCaptureThreadGlobal; + callbacks[131] = (delegate* unmanaged)&_getHelperFtn; + callbacks[132] = (delegate* unmanaged)&_getFunctionEntryPoint; + callbacks[133] = (delegate* unmanaged)&_getFunctionFixedEntryPoint; + callbacks[134] = (delegate* unmanaged)&_getMethodSync; + callbacks[135] = (delegate* unmanaged)&_getLazyStringLiteralHelper; + callbacks[136] = (delegate* unmanaged)&_embedModuleHandle; + callbacks[137] = (delegate* unmanaged)&_embedClassHandle; + callbacks[138] = (delegate* unmanaged)&_embedMethodHandle; + callbacks[139] = (delegate* unmanaged)&_embedFieldHandle; + callbacks[140] = (delegate* unmanaged)&_embedGenericHandle; + callbacks[141] = (delegate* unmanaged)&_getLocationOfThisType; + callbacks[142] = (delegate* unmanaged)&_getAddressOfPInvokeTarget; + callbacks[143] = (delegate* unmanaged)&_GetCookieForPInvokeCalliSig; + callbacks[144] = (delegate* unmanaged)&_canGetCookieForPInvokeCalliSig; + callbacks[145] = (delegate* unmanaged)&_getJustMyCodeHandle; + callbacks[146] = (delegate* unmanaged)&_GetProfilingHandle; + callbacks[147] = (delegate* unmanaged)&_getCallInfo; + callbacks[148] = (delegate* unmanaged)&_canAccessFamily; + callbacks[149] = (delegate* unmanaged)&_isRIDClassDomainID; + callbacks[150] = (delegate* unmanaged)&_getClassDomainID; + callbacks[151] = (delegate* unmanaged)&_getFieldAddress; + callbacks[152] = (delegate* unmanaged)&_getReadonlyStaticFieldValue; + callbacks[153] = (delegate* unmanaged)&_getStaticFieldCurrentClass; + callbacks[154] = (delegate* unmanaged)&_getVarArgsHandle; + callbacks[155] = (delegate* unmanaged)&_canGetVarArgsHandle; + callbacks[156] = (delegate* unmanaged)&_constructStringLiteral; + callbacks[157] = (delegate* unmanaged)&_emptyStringLiteral; + callbacks[158] = (delegate* unmanaged)&_getFieldThreadLocalStoreID; + callbacks[159] = (delegate* unmanaged)&_addActiveDependency; + callbacks[160] = (delegate* unmanaged)&_GetDelegateCtor; + callbacks[161] = (delegate* unmanaged)&_MethodCompileComplete; + callbacks[162] = (delegate* unmanaged)&_getTailCallHelpers; + callbacks[163] = (delegate* unmanaged)&_convertPInvokeCalliToCall; + callbacks[164] = (delegate* unmanaged)&_notifyInstructionSetUsage; + callbacks[165] = (delegate* unmanaged)&_updateEntryPointForTailCall; + callbacks[166] = (delegate* unmanaged)&_allocMem; + callbacks[167] = (delegate* unmanaged)&_reserveUnwindInfo; + callbacks[168] = (delegate* unmanaged)&_allocUnwindInfo; + callbacks[169] = (delegate* unmanaged)&_allocGCInfo; + callbacks[170] = (delegate* unmanaged)&_setEHcount; + callbacks[171] = (delegate* unmanaged)&_setEHinfo; + callbacks[172] = (delegate* unmanaged)&_logMsg; + callbacks[173] = (delegate* unmanaged)&_doAssert; + callbacks[174] = (delegate* unmanaged)&_reportFatalError; + callbacks[175] = (delegate* unmanaged)&_getPgoInstrumentationResults; + callbacks[176] = (delegate* unmanaged)&_allocPgoInstrumentationBySchema; + callbacks[177] = (delegate* unmanaged)&_recordCallSite; + callbacks[178] = (delegate* unmanaged)&_recordRelocation; + callbacks[179] = (delegate* unmanaged)&_getRelocTypeHint; + callbacks[180] = (delegate* unmanaged)&_getExpectedTargetArchitecture; + callbacks[181] = (delegate* unmanaged)&_getJitFlags; return (IntPtr)callbacks; } diff --git a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt index e120cacc136f6..d33bea88a4c5d 100644 --- a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt +++ b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt @@ -240,6 +240,7 @@ FUNCTIONS TypeCompareState compareTypesForEquality(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2) CORINFO_CLASS_HANDLE mergeClasses(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2) bool isMoreSpecificType(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2) + TypeCompareState isEnum(CORINFO_CLASS_HANDLE cls, CORINFO_CLASS_HANDLE* underlyingType) CORINFO_CLASS_HANDLE getParentType(CORINFO_CLASS_HANDLE cls) CorInfoType getChildType(CORINFO_CLASS_HANDLE clsHnd, CORINFO_CLASS_HANDLE* clsRet) bool satisfiesClassConstraints(CORINFO_CLASS_HANDLE cls) diff --git a/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h b/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h index fb7889cbb9d9f..4f2f5944d2d6d 100644 --- a/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h +++ b/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h @@ -95,6 +95,7 @@ struct JitInterfaceCallbacks TypeCompareState (* compareTypesForEquality)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2); CORINFO_CLASS_HANDLE (* mergeClasses)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2); bool (* isMoreSpecificType)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2); + TypeCompareState (* isEnum)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE cls, CORINFO_CLASS_HANDLE* underlyingType); CORINFO_CLASS_HANDLE (* getParentType)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE cls); CorInfoType (* getChildType)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE clsHnd, CORINFO_CLASS_HANDLE* clsRet); bool (* satisfiesClassConstraints)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE cls); @@ -1025,6 +1026,16 @@ class JitInterfaceWrapper : public ICorJitInfo return temp; } + virtual TypeCompareState isEnum( + CORINFO_CLASS_HANDLE cls, + CORINFO_CLASS_HANDLE* underlyingType) +{ + CorInfoExceptionClass* pException = nullptr; + TypeCompareState temp = _callbacks->isEnum(_thisHandle, &pException, cls, underlyingType); + if (pException != nullptr) throw pException; + return temp; +} + virtual CORINFO_CLASS_HANDLE getParentType( CORINFO_CLASS_HANDLE cls) { diff --git a/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h b/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h index dab1ac0c1a064..588d96884df20 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h @@ -159,6 +159,7 @@ LWM(IsValidToken, DLD, DWORD) LWM(IsValueClass, DWORDLONG, DWORD) LWM(MergeClasses, DLDL, DWORDLONG) LWM(IsMoreSpecificType, DLDL, DWORD) +LWM(IsEnum, DWORDLONG, DLD) LWM(PInvokeMarshalingRequired, MethodOrSigInfoValue, DWORD) LWM(ResolveToken, Agnostic_CORINFO_RESOLVED_TOKENin, ResolveTokenValue) LWM(ResolveVirtualMethod, Agnostic_ResolveVirtualMethodKey, Agnostic_ResolveVirtualMethodResult) diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp index c85c3dd823c47..cb6db7560e21f 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp @@ -5911,6 +5911,41 @@ bool MethodContext::repIsMoreSpecificType(CORINFO_CLASS_HANDLE cls1, CORINFO_CLA return value != 0; } +void MethodContext::recIsEnum(CORINFO_CLASS_HANDLE cls, CORINFO_CLASS_HANDLE* underlyingType, TypeCompareState result) +{ + if (IsEnum == nullptr) + IsEnum = new LightWeightMap(); + + DWORDLONG key = CastHandle(cls); + + DLD value; + ZeroMemory(&value, sizeof(value)); + if (underlyingType != nullptr) + value.A = CastHandle(*underlyingType); + else + value.A = 0; + value.B = (DWORD)result; + + IsEnum->Add(key, value); + DEBUG_REC(dmpIsEnum(key, value)); +} +void MethodContext::dmpIsEnum(DWORDLONG key, DLD value) +{ + printf("IsEnum key cls-%016llX, value underlyingType-%016llX result-%u", key, value.A, value.B); +} +TypeCompareState MethodContext::repIsEnum(CORINFO_CLASS_HANDLE cls, CORINFO_CLASS_HANDLE* underlyingType) +{ + DWORDLONG key = CastHandle(cls); + + AssertMapAndKeyExist(IsEnum, key, ": key %016llX", key); + + DLD value = IsEnum->Get(key); + DEBUG_REP(dmpIsEnum(key, value)); + if (underlyingType != nullptr) + *underlyingType = (CORINFO_CLASS_HANDLE)value.A; + return (TypeCompareState)value.B; +} + void MethodContext::recGetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig, void** ppIndirection, LPVOID result) { if (GetCookieForPInvokeCalliSig == nullptr) diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h index c3e2fdabfde69..184102f757d8c 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h @@ -744,6 +744,10 @@ class MethodContext void dmpIsMoreSpecificType(DLDL key, DWORD value); bool repIsMoreSpecificType(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2); + void recIsEnum(CORINFO_CLASS_HANDLE cls, CORINFO_CLASS_HANDLE* underlyingType, TypeCompareState result); + void dmpIsEnum(DWORDLONG key, DLD value); + TypeCompareState repIsEnum(CORINFO_CLASS_HANDLE cls, CORINFO_CLASS_HANDLE* underlyingType); + void recGetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig, void** ppIndirection, LPVOID result); void dmpGetCookieForPInvokeCalliSig(const GetCookieForPInvokeCalliSigValue& key, DLDL value); LPVOID repGetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig, void** ppIndirection); @@ -1154,6 +1158,7 @@ enum mcPackets Packet_IsObjectImmutable = 200, Packet_ExpandRawHandleIntrinsic = 201, Packet_GetArrayOrStringLength = 202, + Packet_IsEnum = 203, }; void SetDebugDumpVariables(); diff --git a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp index 75d5a6f43749c..9e91b15de69fe 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp @@ -968,6 +968,24 @@ bool interceptor_ICJI::isMoreSpecificType(CORINFO_CLASS_HANDLE cls1, CORINFO_CLA return temp; } +// Returns TypeCompareState::Must if cls is known to be an enum. +// For enums with known exact type returns the underlying +// type in underlyingType when the provided pointer is +// non-NULL. +// Returns TypeCompareState::May when a runtime check is required. +TypeCompareState interceptor_ICJI::isEnum(CORINFO_CLASS_HANDLE cls, CORINFO_CLASS_HANDLE* underlyingType) +{ + mc->cr->AddCall("isEnum"); + CORINFO_CLASS_HANDLE tempUnderlyingType = nullptr; + TypeCompareState temp = original_ICorJitInfo->isEnum(cls, &tempUnderlyingType); + mc->recIsEnum(cls, &tempUnderlyingType, temp); + if (underlyingType != nullptr) + { + *underlyingType = tempUnderlyingType; + } + return temp; +} + // Given a class handle, returns the Parent type. // For COMObjectType, it returns Class Handle of System.Object. // Returns 0 if System.Object is passed in. diff --git a/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp b/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp index c9bb221333a32..2ecdf378351f5 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp @@ -679,6 +679,14 @@ bool interceptor_ICJI::isMoreSpecificType( return original_ICorJitInfo->isMoreSpecificType(cls1, cls2); } +TypeCompareState interceptor_ICJI::isEnum( + CORINFO_CLASS_HANDLE cls, + CORINFO_CLASS_HANDLE* underlyingType) +{ + mcs->AddCall("isEnum"); + return original_ICorJitInfo->isEnum(cls, underlyingType); +} + CORINFO_CLASS_HANDLE interceptor_ICJI::getParentType( CORINFO_CLASS_HANDLE cls) { diff --git a/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp b/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp index 6f5367b1fbff6..1e0593ec3258a 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp @@ -595,6 +595,13 @@ bool interceptor_ICJI::isMoreSpecificType( return original_ICorJitInfo->isMoreSpecificType(cls1, cls2); } +TypeCompareState interceptor_ICJI::isEnum( + CORINFO_CLASS_HANDLE cls, + CORINFO_CLASS_HANDLE* underlyingType) +{ + return original_ICorJitInfo->isEnum(cls, underlyingType); +} + CORINFO_CLASS_HANDLE interceptor_ICJI::getParentType( CORINFO_CLASS_HANDLE cls) { diff --git a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp index cebb456c7afeb..a86f7d2718424 100644 --- a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp @@ -819,6 +819,17 @@ bool MyICJI::isMoreSpecificType(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE return jitInstance->mc->repIsMoreSpecificType(cls1, cls2); } +// Returns TypeCompareState::Must if cls is known to be an enum. +// For enums with known exact type returns the underlying +// type in underlyingType when the provided pointer is +// non-NULL. +// Returns TypeCompareState::May when a runtime check is required. +TypeCompareState MyICJI::isEnum(CORINFO_CLASS_HANDLE cls, CORINFO_CLASS_HANDLE* underlyingType) +{ + jitInstance->mc->cr->AddCall("isEnum"); + return jitInstance->mc->repIsEnum(cls, underlyingType); +} + // Given a class handle, returns the Parent type. // For COMObjectType, it returns Class Handle of System.Object. // Returns 0 if System.Object is passed in. diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 3d0012cbd4119..9c8d9cbe59c0b 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -4528,6 +4528,57 @@ bool CEEInfo::isMoreSpecificType( return result; } +/*********************************************************************/ +// Returns TypeCompareState::Must if cls is known to be an enum. +// For enums with known exact type returns the underlying +// type in underlyingType when the provided pointer is +// non-NULL. +// Returns TypeCompareState::May when a runtime check is required. +TypeCompareState CEEInfo::isEnum( + CORINFO_CLASS_HANDLE cls, + CORINFO_CLASS_HANDLE* underlyingType) +{ + CONTRACTL { + THROWS; + GC_TRIGGERS; + MODE_PREEMPTIVE; + } CONTRACTL_END; + + TypeCompareState result = TypeCompareState::May; + + if (underlyingType != nullptr) + { + *underlyingType = nullptr; + } + + JIT_TO_EE_TRANSITION_LEAF(); + + TypeHandle th(cls); + + _ASSERTE(!th.IsNull()); + + if (!th.IsGenericVariable()) + { + if (!th.IsTypeDesc() && th.AsMethodTable()->IsEnum()) + { + result = TypeCompareState::Must; + if (underlyingType != nullptr) + { + CorElementType elemType = th.AsMethodTable()->GetInternalCorElementType(); + TypeHandle underlyingHandle(CoreLibBinder::GetElementType(elemType)); + *underlyingType = CORINFO_CLASS_HANDLE(underlyingHandle.AsPtr()); + } + } + else + { + result = TypeCompareState::MustNot; + } + } + + EE_TO_JIT_TRANSITION_LEAF(); + return result; +} + /*********************************************************************/ // Given a class handle, returns the Parent type. // For COMObjectType, it returns Class Handle of System.Object. diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/EqualityComparer.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/EqualityComparer.cs index fbee608bbf480..aedbfa953bf88 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/EqualityComparer.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/EqualityComparer.cs @@ -243,7 +243,7 @@ private EnumEqualityComparer(SerializationInfo information, StreamingContext con public void GetObjectData(SerializationInfo info, StreamingContext context) { // For back-compat we need to serialize the comparers for enums with underlying types other than int as ObjectEqualityComparer - if (Type.GetTypeCode(Enum.GetUnderlyingType(typeof(T))) != TypeCode.Int32) + if (Type.GetTypeCode(typeof(T)) != TypeCode.Int32) { info.SetType(typeof(ObjectEqualityComparer)); } 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 5b52162fa14af..47014ec5d718f 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 @@ -103,6 +103,9 @@ internal static bool IsPrimitiveType(this CorElementType et) // The following intrinsics return true if input is a compile-time constant // Feel free to add more overloads on demand #pragma warning disable IDE0060 + [Intrinsic] + internal static bool IsKnownConstant(Type? t) => false; + [Intrinsic] internal static bool IsKnownConstant(string? t) => false; diff --git a/src/libraries/System.Private.CoreLib/src/System/RuntimeType.cs b/src/libraries/System.Private.CoreLib/src/System/RuntimeType.cs index d4b45f21bc9d7..7472df80e998e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/RuntimeType.cs +++ b/src/libraries/System.Private.CoreLib/src/System/RuntimeType.cs @@ -267,66 +267,7 @@ protected override TypeCode GetTypeCodeImpl() if (typeCode != TypeCode.Empty) return typeCode; - CorElementType corElementType = RuntimeTypeHandle.GetCorElementType(this); - switch (corElementType) - { - case CorElementType.ELEMENT_TYPE_BOOLEAN: - typeCode = TypeCode.Boolean; break; - case CorElementType.ELEMENT_TYPE_CHAR: - typeCode = TypeCode.Char; break; - case CorElementType.ELEMENT_TYPE_I1: - typeCode = TypeCode.SByte; break; - case CorElementType.ELEMENT_TYPE_U1: - typeCode = TypeCode.Byte; break; - case CorElementType.ELEMENT_TYPE_I2: - typeCode = TypeCode.Int16; break; - case CorElementType.ELEMENT_TYPE_U2: - typeCode = TypeCode.UInt16; break; - case CorElementType.ELEMENT_TYPE_I4: - typeCode = TypeCode.Int32; break; - case CorElementType.ELEMENT_TYPE_U4: - typeCode = TypeCode.UInt32; break; - case CorElementType.ELEMENT_TYPE_I8: - typeCode = TypeCode.Int64; break; - case CorElementType.ELEMENT_TYPE_U8: - typeCode = TypeCode.UInt64; break; - case CorElementType.ELEMENT_TYPE_R4: - typeCode = TypeCode.Single; break; - case CorElementType.ELEMENT_TYPE_R8: - typeCode = TypeCode.Double; break; -#if !CORECLR - case CorElementType.ELEMENT_TYPE_STRING: - typeCode = TypeCode.String; break; -#endif - case CorElementType.ELEMENT_TYPE_VALUETYPE: - if (ReferenceEquals(this, typeof(decimal))) - typeCode = TypeCode.Decimal; - else if (ReferenceEquals(this, typeof(DateTime))) - typeCode = TypeCode.DateTime; - else if (IsActualEnum) - typeCode = GetTypeCode(Enum.InternalGetUnderlyingType(this)); - else - typeCode = TypeCode.Object; - break; - default: -#if CORECLR - // GetSignatureCorElementType returns E_T_CLASS for E_T_STRING - if (ReferenceEquals(this, typeof(string))) - { - typeCode = TypeCode.String; - break; - } -#endif - if (ReferenceEquals(this, typeof(DBNull))) - { - typeCode = TypeCode.DBNull; - break; - } - - typeCode = TypeCode.Object; - break; - } - + typeCode = Type.GetRuntimeTypeCode(this); Cache.TypeCode = typeCode; return typeCode; diff --git a/src/libraries/System.Private.CoreLib/src/System/Type.cs b/src/libraries/System.Private.CoreLib/src/System/Type.cs index 5a7ebed457d7d..0ab6e955e37d9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Type.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Type.cs @@ -104,7 +104,7 @@ public virtual Type[] GetGenericParameterConstraints() public bool IsContextful => IsContextfulImpl(); protected virtual bool IsContextfulImpl() => false; - public virtual bool IsEnum => IsSubclassOf(typeof(Enum)); + public virtual bool IsEnum { [Intrinsic] get => IsSubclassOf(typeof(Enum)); } public bool IsMarshalByRef => IsMarshalByRefImpl(); protected virtual bool IsMarshalByRefImpl() => false; public bool IsPrimitive => IsPrimitiveImpl(); @@ -438,11 +438,59 @@ public static Type[] GetTypeArray(object[] args) return cls; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TypeCode GetTypeCode(Type? type) { + if (RuntimeHelpers.IsKnownConstant(type) && type is RuntimeType) + { + return GetRuntimeTypeCode((RuntimeType)type); + } return type?.GetTypeCodeImpl() ?? TypeCode.Empty; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static TypeCode GetRuntimeTypeCode(RuntimeType type) + { + RuntimeType underlyingType = type; + if (type.IsActualEnum) + underlyingType = (RuntimeType)type.GetEnumUnderlyingType(); + + if (underlyingType == typeof(sbyte)) + return TypeCode.SByte; + else if (underlyingType == typeof(byte)) + return TypeCode.Byte; + else if (underlyingType == typeof(short)) + return TypeCode.Int16; + else if (underlyingType == typeof(ushort)) + return TypeCode.UInt16; + else if (underlyingType == typeof(int)) + return TypeCode.Int32; + else if (underlyingType == typeof(uint)) + return TypeCode.UInt32; + else if (underlyingType == typeof(long)) + return TypeCode.Int64; + else if (underlyingType == typeof(ulong)) + return TypeCode.UInt64; + else if (underlyingType == typeof(bool)) + return TypeCode.Boolean; + else if (underlyingType == typeof(char)) + return TypeCode.Char; + else if (underlyingType == typeof(float)) + return TypeCode.Single; + else if (underlyingType == typeof(double)) + return TypeCode.Double; + else if (underlyingType == typeof(decimal)) + return TypeCode.Decimal; + else if (underlyingType == typeof(DateTime)) + return TypeCode.DateTime; + else if (underlyingType == typeof(string)) + return TypeCode.String; + else if (underlyingType == typeof(DBNull)) + return TypeCode.DBNull; + else + return TypeCode.Object; + } + protected virtual TypeCode GetTypeCodeImpl() { Type systemType = UnderlyingSystemType; @@ -503,6 +551,7 @@ protected virtual TypeCode GetTypeCodeImpl() [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2085:UnrecognizedReflectionPattern", Justification = "The single instance field on enum types is never trimmed")] + [Intrinsic] public virtual Type GetEnumUnderlyingType() { if (!IsEnum) diff --git a/src/libraries/System.Runtime/tests/System/EnumTests.cs b/src/libraries/System.Runtime/tests/System/EnumTests.cs index a243e6c7c8bff..a4cbc4a52ac17 100644 --- a/src/libraries/System.Runtime/tests/System/EnumTests.cs +++ b/src/libraries/System.Runtime/tests/System/EnumTests.cs @@ -464,7 +464,7 @@ public static IEnumerable GetName_CharEnum_TestData() yield return new object[] { (char)2, "Value2" }; yield return new object[] { (char)4, null }; } - + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] [MemberData(nameof(GetName_CharEnum_TestData))] public void GetName_InvokeCharEnum_ReturnsExpected(object value, string expected) @@ -479,7 +479,7 @@ public static IEnumerable GetName_BoolEnum_TestData() yield return new object[] { true, "Value1" }; yield return new object[] { false, "Value2" }; } - + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] [MemberData(nameof(GetName_BoolEnum_TestData))] public void GetName_InvokeBoolEnum_ReturnsExpected(object value, string expected) @@ -1443,7 +1443,7 @@ public static void GetNames_NullEnumType_ThrowsArgumentNullException() { AssertExtensions.Throws("enumType", () => Enum.GetNames(null)); } - + [Theory] [InlineData(typeof(object))] [InlineData(typeof(int))] diff --git a/src/tests/JIT/Intrinsics/TypeIntrinsics.GetEnumUnderlyingType.cs b/src/tests/JIT/Intrinsics/TypeIntrinsics.GetEnumUnderlyingType.cs new file mode 100644 index 0000000000000..93142921f1d73 --- /dev/null +++ b/src/tests/JIT/Intrinsics/TypeIntrinsics.GetEnumUnderlyingType.cs @@ -0,0 +1,111 @@ +// 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.Collections; +using System.Collections.Generic; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; + +public class GetEnumUnderlyingType +{ + public static void TestGetEnumUnderlyingType() + { + AssertEquals(typeof(sbyte), typeof(SByteEnum).GetEnumUnderlyingType()); + AssertEquals(typeof(byte), typeof(ByteEnum).GetEnumUnderlyingType()); + AssertEquals(typeof(short), typeof(ShortEnum).GetEnumUnderlyingType()); + AssertEquals(typeof(ushort), typeof(UShortEnum).GetEnumUnderlyingType()); + AssertEquals(typeof(int), typeof(IntEnum).GetEnumUnderlyingType()); + AssertEquals(typeof(uint), typeof(UIntEnum).GetEnumUnderlyingType()); + AssertEquals(typeof(long), typeof(LongEnum).GetEnumUnderlyingType()); + AssertEquals(typeof(ulong), typeof(ULongEnum).GetEnumUnderlyingType()); + + AssertEquals(typeof(char), typeof(CharEnum).GetEnumUnderlyingType()); + AssertEquals(typeof(bool), typeof(BoolEnum).GetEnumUnderlyingType()); + AssertEquals(typeof(float), typeof(FloatEnum).GetEnumUnderlyingType()); + AssertEquals(typeof(double), typeof(DoubleEnum).GetEnumUnderlyingType()); + AssertEquals(typeof(nint), typeof(IntPtrEnum).GetEnumUnderlyingType()); + AssertEquals(typeof(nuint), typeof(UIntPtrEnum).GetEnumUnderlyingType()); + + AssertThrowsArgumentException(() => typeof(int).GetEnumUnderlyingType()); + AssertThrowsArgumentException(() => typeof(nint).GetEnumUnderlyingType()); + AssertThrowsArgumentException(() => typeof(Enum).GetEnumUnderlyingType()); + AssertThrowsArgumentException(() => typeof(object).GetEnumUnderlyingType()); + AssertThrowsNullReferenceException(() => ((Type)null).GetEnumUnderlyingType()); + + AssertEquals(typeof(sbyte), NoInline(typeof(SByteEnum).GetEnumUnderlyingType())); + AssertEquals(typeof(byte), NoInline(typeof(ByteEnum).GetEnumUnderlyingType())); + AssertEquals(typeof(short), NoInline(typeof(ShortEnum).GetEnumUnderlyingType())); + AssertEquals(typeof(ushort), NoInline(typeof(UShortEnum).GetEnumUnderlyingType())); + AssertEquals(typeof(int), NoInline(typeof(IntEnum).GetEnumUnderlyingType())); + AssertEquals(typeof(uint), NoInline(typeof(UIntEnum).GetEnumUnderlyingType())); + AssertEquals(typeof(long), NoInline(typeof(LongEnum).GetEnumUnderlyingType())); + AssertEquals(typeof(ulong), NoInline(typeof(ULongEnum).GetEnumUnderlyingType())); + + AssertEquals(typeof(char), NoInline(typeof(CharEnum).GetEnumUnderlyingType())); + AssertEquals(typeof(bool), NoInline(typeof(BoolEnum).GetEnumUnderlyingType())); + AssertEquals(typeof(float), NoInline(typeof(FloatEnum).GetEnumUnderlyingType())); + AssertEquals(typeof(double), NoInline(typeof(DoubleEnum).GetEnumUnderlyingType())); + AssertEquals(typeof(nint), NoInline(typeof(IntPtrEnum).GetEnumUnderlyingType())); + AssertEquals(typeof(nuint), NoInline(typeof(UIntPtrEnum).GetEnumUnderlyingType())); + + AssertThrowsArgumentException(() => NoInline(typeof(int).GetEnumUnderlyingType())); + AssertThrowsArgumentException(() => NoInline(typeof(nint).GetEnumUnderlyingType())); + AssertThrowsArgumentException(() => NoInline(typeof(Enum).GetEnumUnderlyingType())); + AssertThrowsArgumentException(() => NoInline(typeof(object).GetEnumUnderlyingType())); + AssertThrowsNullReferenceException(() => NoInline(null).GetEnumUnderlyingType()); + + AssertThrowsArgumentException(() => typeof(GenericEnumClass<>).GetGenericArguments()[0].GetEnumUnderlyingType()); + } + + public enum SByteEnum : sbyte {} + public enum ByteEnum : byte {} + public enum ShortEnum : short {} + public enum UShortEnum : ushort {} + public enum IntEnum {} + public enum UIntEnum : uint {} + public enum LongEnum : long {} + public enum ULongEnum : ulong {} + + public class GenericEnumClass where T : Enum + { + public T field; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static Type NoInline(Type type) => type; + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void AssertEquals(Type expected, Type actual, [CallerLineNumber] int l = 0) + { + if (expected != actual) + throw new InvalidOperationException($"Invalid type, expected {expected.FullName}, got {actual.FullName} at line {l}"); + } + + private static void AssertThrowsArgumentException(Action a, [CallerLineNumber] int l = 0) + { + try + { + a(); + } + catch (ArgumentException) + { + return; + } + throw new InvalidOperationException($"Expected ArgumentException at line {l}"); + } + + private static void AssertThrowsNullReferenceException(Action a, [CallerLineNumber] int l = 0) + { + try + { + a(); + } + catch (NullReferenceException) + { + return; + } + throw new InvalidOperationException($"Expected NullReferenceException at line {l}"); + } +} diff --git a/src/tests/JIT/Intrinsics/TypeIntrinsics.IsAssignableFrom.cs b/src/tests/JIT/Intrinsics/TypeIntrinsics.IsAssignableFrom.cs index d6520d30f9b7e..706cae6a8e83f 100644 --- a/src/tests/JIT/Intrinsics/TypeIntrinsics.IsAssignableFrom.cs +++ b/src/tests/JIT/Intrinsics/TypeIntrinsics.IsAssignableFrom.cs @@ -71,7 +71,7 @@ public static void TestIsAssignableFrom() IsFalse(typeof(float).IsAssignableFrom(typeof(SimpleEnum_uint))); IsFalse(typeof(SimpleEnum_uint).IsAssignableFrom(typeof(ValueType))); - // Covariance/Contravariance + // Covariance/Contravariance IsTrue (typeof(IEnumerable).IsAssignableFrom(typeof(List))); IsTrue (typeof(IEnumerable).IsAssignableFrom(typeof(List))); IsTrue (typeof(IEnumerable).IsAssignableFrom(typeof(IList))); @@ -166,6 +166,20 @@ public static void TestIsAssignableFrom() IsFalse(typeof(Vector128).IsAssignableFrom(typeof(Vector))); IsFalse(typeof(Vector256).IsAssignableFrom(typeof(Vector))); + // null type + IsFalse(typeof(int).IsAssignableFrom(null)); + IsFalse(typeof(object).IsAssignableFrom(null)); + ThrowsNRE(() => { _ = ((Type)null).IsAssignableFrom(typeof(int)); }); + ThrowsNRE(() => { _ = ((Type)null).IsAssignableFrom(typeof(object)); }); + ThrowsNRE(() => { _ = ((Type)null).IsAssignableFrom(null); }); + ThrowsNRE(() => { _ = ((Type)null).IsAssignableFrom(null); }); + IsFalse(typeof(int).IsAssignableFrom(__reftype(default))); + IsFalse(typeof(object).IsAssignableFrom(__reftype(default))); + ThrowsNRE(() => { _ = __reftype(default).IsAssignableFrom(typeof(int)); }); + ThrowsNRE(() => { _ = __reftype(default).IsAssignableFrom(typeof(object)); }); + ThrowsNRE(() => { _ = __reftype(default).IsAssignableFrom(__reftype(default)); }); + ThrowsNRE(() => { _ = __reftype(default).IsAssignableFrom(__reftype(default)); }); + // System.__Canon IsTrue (IsAssignableFrom, KeyValuePair>()); IsTrue (IsAssignableFrom, KeyValuePair>()); diff --git a/src/tests/JIT/Intrinsics/TypeIntrinsics.IsAssignableTo.cs b/src/tests/JIT/Intrinsics/TypeIntrinsics.IsAssignableTo.cs index 6a7ccc73e362b..d5bb363d8e221 100644 --- a/src/tests/JIT/Intrinsics/TypeIntrinsics.IsAssignableTo.cs +++ b/src/tests/JIT/Intrinsics/TypeIntrinsics.IsAssignableTo.cs @@ -71,7 +71,7 @@ public static void TestIsAssignableTo() IsFalse(typeof(SimpleEnum_uint).IsAssignableTo(typeof(float))); IsFalse(typeof(ValueType).IsAssignableTo(typeof(SimpleEnum_uint))); - // Covariance/Contravariance + // Covariance/Contravariance IsTrue (typeof(List).IsAssignableTo(typeof(IEnumerable))); IsTrue (typeof(List).IsAssignableTo(typeof(IEnumerable))); IsTrue (typeof(IList).IsAssignableTo(typeof(IEnumerable))); @@ -166,6 +166,20 @@ public static void TestIsAssignableTo() IsFalse(typeof(Vector).IsAssignableTo(typeof(Vector128))); IsFalse(typeof(Vector).IsAssignableTo(typeof(Vector256))); + // null type + IsFalse(typeof(int).IsAssignableTo(null)); + IsFalse(typeof(object).IsAssignableTo(null)); + ThrowsNRE(() => { _ = ((Type)null).IsAssignableTo(typeof(int)); }); + ThrowsNRE(() => { _ = ((Type)null).IsAssignableTo(typeof(object)); }); + ThrowsNRE(() => { _ = ((Type)null).IsAssignableTo(null); }); + ThrowsNRE(() => { _ = ((Type)null).IsAssignableTo(null); }); + IsFalse(typeof(int).IsAssignableTo(__reftype(default))); + IsFalse(typeof(object).IsAssignableTo(__reftype(default))); + ThrowsNRE(() => { _ = __reftype(default).IsAssignableTo(typeof(int)); }); + ThrowsNRE(() => { _ = __reftype(default).IsAssignableTo(typeof(object)); }); + ThrowsNRE(() => { _ = __reftype(default).IsAssignableTo(__reftype(default)); }); + ThrowsNRE(() => { _ = __reftype(default).IsAssignableTo(__reftype(default)); }); + // System.__Canon IsTrue (IsAssignableTo, KeyValuePair>()); IsTrue (IsAssignableTo, KeyValuePair>()); diff --git a/src/tests/JIT/Intrinsics/TypeIntrinsics.cs b/src/tests/JIT/Intrinsics/TypeIntrinsics.cs index 0c91318abb34f..d5a99d7392fd4 100644 --- a/src/tests/JIT/Intrinsics/TypeIntrinsics.cs +++ b/src/tests/JIT/Intrinsics/TypeIntrinsics.cs @@ -92,12 +92,43 @@ public static int Main(string[] args) IsTrue (IsValueTypeRef(ref _varGenericStructStr)); IsTrue (IsValueTypeRef(ref _varEnum)); + // test __reftype + IsTrue (__reftype(__makeref(_varInt)).IsValueType); + IsFalse(__reftype(__makeref(_varObject)).IsValueType); + ThrowsNRE(() => { IsValueType(_varNullableIntNull); }); ThrowsNRE(() => { IsValueType(_varStringNull); }); ThrowsNRE(() => { IsValueTypeRef(ref _varNullableIntNull); }); ThrowsNRE(() => { IsValueTypeRef(ref _varStringNull); }); + ThrowsNRE(() => { _ = Type.GetTypeFromHandle(default).IsValueType; }); + ThrowsNRE(() => { _ = Type.GetTypeFromHandle(new RuntimeTypeHandle()).IsValueType; }); + ThrowsNRE(() => { _ = __reftype(default).IsValueType; }); TestIsAssignableFrom(); + TestIsAssignableTo(); + + IsFalse(typeof(byte).IsEnum); + IsFalse(typeof(int).IsEnum); + IsFalse(typeof(int?).IsEnum); + IsFalse(typeof(int*).IsEnum); + IsFalse(typeof(nint).IsEnum); + IsFalse(typeof(void).IsEnum); + IsFalse(typeof(object).IsEnum); + IsFalse(typeof(Enum).IsEnum); + IsFalse(typeof(ValueType).IsEnum); + IsFalse(typeof(GenericStruct).IsEnum); + IsFalse(typeof(SimpleStruct).IsEnum); + IsTrue (typeof(SimpleEnum).IsEnum); + IsTrue (typeof(CharEnum).IsEnum); + IsTrue (typeof(BoolEnum).IsEnum); + IsTrue (typeof(FloatEnum).IsEnum); + IsTrue (typeof(DoubleEnum).IsEnum); + IsTrue (typeof(IntPtrEnum).IsEnum); + IsTrue (typeof(UIntPtrEnum).IsEnum); + + IsTrue(typeof(GenericEnumClass<>).GetGenericArguments()[0].IsEnum); + + GetEnumUnderlyingType.TestGetEnumUnderlyingType(); return 100 + _errors; } @@ -135,7 +166,6 @@ public static int Main(string[] args) [MethodImpl(MethodImplOptions.NoInlining)] private static dynamic CreateDynamic2() => new { Name = "Test" }; - static void IsTrue(bool expression, [CallerLineNumber] int line = 0, [CallerFilePath] string file = "") { if (!expression) @@ -169,14 +199,25 @@ static void ThrowsNRE(Action action, [CallerLineNumber] int line = 0, [CallerFil Console.WriteLine($"{file}:L{line} {exc}"); } Console.WriteLine($"Line {line}: test failed (expected: NullReferenceException)"); + _errors++; } } +public class GenericEnumClass where T : Enum +{ + public T field; +} + public struct GenericStruct { public T field; } +public struct SimpleStruct +{ + public int field; +} + public enum SimpleEnum { A,B,C diff --git a/src/tests/JIT/Intrinsics/TypeIntrinsicsEnums.il b/src/tests/JIT/Intrinsics/TypeIntrinsicsEnums.il new file mode 100644 index 0000000000000..36f9f07dd8fdc --- /dev/null +++ b/src/tests/JIT/Intrinsics/TypeIntrinsicsEnums.il @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +.assembly extern System.Runtime { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) } + +.assembly TypeIntrinsicsEnums { } + +.class public auto ansi sealed CharEnum + extends [System.Runtime]System.Enum +{ + .field public specialname rtspecialname char value__ +} // end of class CharEnum + +.class public auto ansi sealed BoolEnum + extends [System.Runtime]System.Enum +{ + .field public specialname rtspecialname bool value__ +} // end of class BoolEnum + +.class public auto ansi sealed FloatEnum + extends [System.Runtime]System.Enum +{ + .field public specialname rtspecialname float32 value__ +} // end of class FloatEnum + +.class public auto ansi sealed DoubleEnum + extends [System.Runtime]System.Enum +{ + .field public specialname rtspecialname float64 value__ +} // end of class DoubleEnum + +.class public auto ansi sealed IntPtrEnum + extends [System.Runtime]System.Enum +{ + .field public specialname rtspecialname native int value__ +} // end of class IntPtrEnum + +.class public auto ansi sealed UIntPtrEnum + extends [System.Runtime]System.Enum +{ + .field public specialname rtspecialname native uint value__ +} // end of class UIntPtrEnum diff --git a/src/tests/JIT/Intrinsics/TypeIntrinsicsEnums.ilproj b/src/tests/JIT/Intrinsics/TypeIntrinsicsEnums.ilproj new file mode 100644 index 0000000000000..05fbf28a4e307 --- /dev/null +++ b/src/tests/JIT/Intrinsics/TypeIntrinsicsEnums.ilproj @@ -0,0 +1,11 @@ + + + Library + true + BuildOnly + false + + + + + diff --git a/src/tests/JIT/Intrinsics/TypeIntrinsics_r.csproj b/src/tests/JIT/Intrinsics/TypeIntrinsics_r.csproj index 9ee21d6e902b9..8a556ce073ac1 100644 --- a/src/tests/JIT/Intrinsics/TypeIntrinsics_r.csproj +++ b/src/tests/JIT/Intrinsics/TypeIntrinsics_r.csproj @@ -4,9 +4,13 @@ None + + + + diff --git a/src/tests/JIT/Intrinsics/TypeIntrinsics_ro.csproj b/src/tests/JIT/Intrinsics/TypeIntrinsics_ro.csproj index d752cddd2a06e..4b1c989578015 100644 --- a/src/tests/JIT/Intrinsics/TypeIntrinsics_ro.csproj +++ b/src/tests/JIT/Intrinsics/TypeIntrinsics_ro.csproj @@ -4,9 +4,13 @@ None True + + + + diff --git a/src/tests/JIT/opt/IsKnownConstant/TypeGetTypeCode.cs b/src/tests/JIT/opt/IsKnownConstant/TypeGetTypeCode.cs new file mode 100644 index 0000000000000..d27b6630120d4 --- /dev/null +++ b/src/tests/JIT/opt/IsKnownConstant/TypeGetTypeCode.cs @@ -0,0 +1,94 @@ +// 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; + +public class Program +{ + public static int Main() + { + AreSame(Type.GetTypeCode(null), TypeCode.Empty); + AreSame(Type.GetTypeCode(typeof(void*)), TypeCode.Object); + AreSame(Type.GetTypeCode(typeof(nint)), TypeCode.Object); + AreSame(Type.GetTypeCode(typeof(nuint)), TypeCode.Object); + AreSame(Type.GetTypeCode(typeof(IntPtr)), TypeCode.Object); + AreSame(Type.GetTypeCode(typeof(UIntPtr)), TypeCode.Object); + AreSame(Type.GetTypeCode(typeof(bool)), TypeCode.Boolean); + AreSame(Type.GetTypeCode(typeof(char)), TypeCode.Char); + AreSame(Type.GetTypeCode(typeof(sbyte)), TypeCode.SByte); + AreSame(Type.GetTypeCode(typeof(byte)), TypeCode.Byte); + AreSame(Type.GetTypeCode(typeof(short)), TypeCode.Int16); + AreSame(Type.GetTypeCode(typeof(ushort)), TypeCode.UInt16); + AreSame(Type.GetTypeCode(typeof(int)), TypeCode.Int32); + AreSame(Type.GetTypeCode(typeof(uint)), TypeCode.UInt32); + AreSame(Type.GetTypeCode(typeof(long)), TypeCode.Int64); + AreSame(Type.GetTypeCode(typeof(ulong)), TypeCode.UInt64); + AreSame(Type.GetTypeCode(typeof(float)), TypeCode.Single); + AreSame(Type.GetTypeCode(typeof(double)), TypeCode.Double); + AreSame(Type.GetTypeCode(typeof(decimal)), TypeCode.Decimal); + AreSame(Type.GetTypeCode(typeof(string)), TypeCode.String); + AreSame(Type.GetTypeCode(typeof(object)), TypeCode.Object); + AreSame(Type.GetTypeCode(typeof(DateTime)), TypeCode.DateTime); + + AreSame(Type.GetTypeCode(typeof(GenericEnumClass<>)), TypeCode.Object); + AreSame(Type.GetTypeCode(typeof(GenericEnumClass)), TypeCode.Object); + AreSame(Type.GetTypeCode(typeof(GenericEnumClass<>).GetGenericArguments()[0]), TypeCode.Object); + + AreSame(Type.GetTypeCode(typeof(SByteEnum)), TypeCode.SByte); + AreSame(Type.GetTypeCode(typeof(ByteEnum)), TypeCode.Byte); + AreSame(Type.GetTypeCode(typeof(ShortEnum)), TypeCode.Int16); + AreSame(Type.GetTypeCode(typeof(UShortEnum)), TypeCode.UInt16); + AreSame(Type.GetTypeCode(typeof(IntEnum)), TypeCode.Int32); + AreSame(Type.GetTypeCode(typeof(UIntEnum)), TypeCode.UInt32); + AreSame(Type.GetTypeCode(typeof(LongEnum)), TypeCode.Int64); + AreSame(Type.GetTypeCode(typeof(ULongEnum)), TypeCode.UInt64); + + AreSame(Type.GetTypeCode(typeof(CharEnum)), TypeCode.Char); + AreSame(Type.GetTypeCode(typeof(BoolEnum)), TypeCode.Boolean); + AreSame(Type.GetTypeCode(typeof(FloatEnum)), TypeCode.Single); + AreSame(Type.GetTypeCode(typeof(DoubleEnum)), TypeCode.Double); + AreSame(Type.GetTypeCode(typeof(IntPtrEnum)), TypeCode.Object); + AreSame(Type.GetTypeCode(typeof(UIntPtrEnum)), TypeCode.Object); + + AreSame(Type.GetTypeCode(NoInline(typeof(string))), TypeCode.String); + AreSame(Type.GetTypeCode(NoInline(typeof(int))), TypeCode.Int32); + AreSame(Type.GetTypeCode(NoInline(typeof(IntEnum))), TypeCode.Int32); + AreSame(Type.GetTypeCode(NoInline(typeof(CharEnum))), TypeCode.Char); + AreSame(Type.GetTypeCode(NoInline(typeof(IntPtrEnum))), TypeCode.Object); + + AreSame(Type.GetTypeCode(__reftype(__makeref(_varInt))), TypeCode.Int32); + AreSame(Type.GetTypeCode(__reftype(__makeref(_varObject))), TypeCode.Object); + + return 100; + } + + private static int _varInt = 42; + private static object _varObject = new object(); + + public enum SByteEnum : sbyte {} + public enum ByteEnum : byte {} + public enum ShortEnum : short {} + public enum UShortEnum : ushort {} + public enum IntEnum {} + public enum UIntEnum : uint {} + public enum LongEnum : long {} + public enum ULongEnum : ulong {} + + public class GenericEnumClass where T : Enum + { + public T field; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static Type NoInline(Type type) => type; + + [MethodImpl(MethodImplOptions.NoInlining)] + static void AreSame(TypeCode a, TypeCode b, [CallerLineNumber] int line = 0) + { + if (a != b) + { + throw new InvalidOperationException($"Invalid TypeCode, expected {b}, got {a} at line {line}"); + } + } +} diff --git a/src/tests/JIT/opt/IsKnownConstant/TypeGetTypeCode.csproj b/src/tests/JIT/opt/IsKnownConstant/TypeGetTypeCode.csproj new file mode 100644 index 0000000000000..5d3631a5de7f5 --- /dev/null +++ b/src/tests/JIT/opt/IsKnownConstant/TypeGetTypeCode.csproj @@ -0,0 +1,12 @@ + + + Exe + True + + + + + + + + diff --git a/src/tests/JIT/opt/IsKnownConstant/TypeGetTypeCodeEnums.il b/src/tests/JIT/opt/IsKnownConstant/TypeGetTypeCodeEnums.il new file mode 100644 index 0000000000000..6a46fa0efec69 --- /dev/null +++ b/src/tests/JIT/opt/IsKnownConstant/TypeGetTypeCodeEnums.il @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +.assembly extern System.Runtime { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) } + +.assembly TypeGetTypeCodeEnums { } + +.class public auto ansi sealed CharEnum + extends [System.Runtime]System.Enum +{ + .field public specialname rtspecialname char value__ +} // end of class CharEnum + +.class public auto ansi sealed BoolEnum + extends [System.Runtime]System.Enum +{ + .field public specialname rtspecialname bool value__ +} // end of class BoolEnum + +.class public auto ansi sealed FloatEnum + extends [System.Runtime]System.Enum +{ + .field public specialname rtspecialname float32 value__ +} // end of class FloatEnum + +.class public auto ansi sealed DoubleEnum + extends [System.Runtime]System.Enum +{ + .field public specialname rtspecialname float64 value__ +} // end of class DoubleEnum + +.class public auto ansi sealed IntPtrEnum + extends [System.Runtime]System.Enum +{ + .field public specialname rtspecialname native int value__ +} // end of class IntPtrEnum + +.class public auto ansi sealed UIntPtrEnum + extends [System.Runtime]System.Enum +{ + .field public specialname rtspecialname native uint value__ +} // end of class UIntPtrEnum diff --git a/src/tests/JIT/opt/IsKnownConstant/TypeGetTypeCodeEnums.ilproj b/src/tests/JIT/opt/IsKnownConstant/TypeGetTypeCodeEnums.ilproj new file mode 100644 index 0000000000000..05fbf28a4e307 --- /dev/null +++ b/src/tests/JIT/opt/IsKnownConstant/TypeGetTypeCodeEnums.ilproj @@ -0,0 +1,11 @@ + + + Library + true + BuildOnly + false + + + + +