Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make Type.IsEnum and Type.GetEnumUnderlyingType intrinsics #71685

Merged
merged 59 commits into from
Nov 11, 2022
Merged
Show file tree
Hide file tree
Changes from 57 commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
2744c1c
Make Type.GetTypeCode an intrinsic
MichalPetryka Jul 5, 2022
f20053b
Change IsEnum to an intrinsic
MichalPetryka Jul 6, 2022
195bad6
Fix formatting
MichalPetryka Jul 6, 2022
f60efb6
Fix formatting 2
MichalPetryka Jul 6, 2022
4317f2d
Formatting fix 3
MichalPetryka Jul 6, 2022
616be5d
Fix test
MichalPetryka Jul 6, 2022
dff9f04
Fallback to the FCall for nint/nuint enums
MichalPetryka Jul 6, 2022
6959e90
Fix compilation
MichalPetryka Jul 6, 2022
c27672c
Add missing Intrinsic
MichalPetryka Jul 6, 2022
1745d84
Fix void handling
MichalPetryka Jul 7, 2022
509842b
Fix compilation
MichalPetryka Jul 7, 2022
e1300f4
Fix formatting
MichalPetryka Jul 7, 2022
94f19da
Add typeof support to IsKnownConstant
MichalPetryka Jul 8, 2022
b07b12f
Merge branch 'main' into enum-underlyingtype
MichalPetryka Jul 8, 2022
c3dc12f
Move the helper
MichalPetryka Jul 8, 2022
eb0bc1b
Add missing break
MichalPetryka Jul 8, 2022
098d472
Revert some changes, simplify code
MichalPetryka Jul 9, 2022
f05b1b2
Use gtIsTypeHandleToRuntimeTypeHelper
MichalPetryka Jul 10, 2022
02cd252
Fix test
MichalPetryka Jul 10, 2022
753dca2
Add __reftype tests
MichalPetryka Jul 10, 2022
b5a2c78
Merge remote-tracking branch 'original/main' into enum-underlyingtype
MichalPetryka Jul 10, 2022
a65d81c
Make more places use gtIsTypeof
MichalPetryka Jul 10, 2022
9f1eb3e
Fix tests
MichalPetryka Jul 10, 2022
4f99338
Move the IsEnum check
MichalPetryka Jul 10, 2022
d2957e7
Update EnumTests.cs
MichalPetryka Jul 10, 2022
791476b
Apply suggestions, add more tests
MichalPetryka Jul 13, 2022
1f2bf41
Add impTypeGetTypeCode to the header
MichalPetryka Jul 13, 2022
d0769b9
Fix formatting
MichalPetryka Jul 13, 2022
2a121d5
Update EnumGetUnderlyingTypeEnums.il
MichalPetryka Jul 13, 2022
9ffdf78
Merge remote-tracking branch 'original/main' into enum-underlyingtype
MichalPetryka Jul 14, 2022
efc9f64
Make the IsActualEnum helper an intrinsic
MichalPetryka Jul 14, 2022
0b3b868
Update EnumGetUnderlyingType.cs
MichalPetryka Jul 14, 2022
9d8b3c6
Fix tests
MichalPetryka Jul 23, 2022
559c815
Merge branch 'main' into enum-underlyingtype
MichalPetryka Aug 19, 2022
7edab59
Merge remote-tracking branch 'original/main' into enum-underlyingtype
MichalPetryka Sep 1, 2022
f2ec8a9
Remove GetTypeCode intrinsics
MichalPetryka Sep 1, 2022
245098c
Merge
MichalPetryka Oct 28, 2022
1a609d1
Create a new JIT-EE api, add GetEnumUnderlyingType back
MichalPetryka Oct 30, 2022
a1291e7
Fix formatting
MichalPetryka Oct 30, 2022
2f17cda
Apply suggestions
MichalPetryka Oct 30, 2022
3e1e52e
Revert unnecessary whitespace
MichalPetryka Oct 30, 2022
e519f0c
Optimize GetTypeCode with IsKnownConstant
MichalPetryka Oct 30, 2022
6d5e308
Fix typo
MichalPetryka Oct 30, 2022
9c70c8f
Change the code to make the inliner happy
MichalPetryka Oct 30, 2022
40a349a
Handle all types in GetTypeCode
MichalPetryka Oct 30, 2022
846e57c
Check for custom types
MichalPetryka Oct 31, 2022
159f147
Apply suggestions from code review
MichalPetryka Oct 31, 2022
c0d9dea
Fix build, do suggested changes
MichalPetryka Oct 31, 2022
b8be7f2
Remove accidental edit
MichalPetryka Oct 31, 2022
7664920
Fix build
MichalPetryka Oct 31, 2022
30afabc
Fix build
MichalPetryka Oct 31, 2022
3e14c6e
Fix build
MichalPetryka Oct 31, 2022
d57b8ae
Fix NativeAOT build
MichalPetryka Nov 1, 2022
ee8ce1d
Merge
MichalPetryka Nov 3, 2022
d2d72df
Remove merge leftovers
MichalPetryka Nov 3, 2022
c1b3bf2
Fix generated files
MichalPetryka Nov 3, 2022
64def53
Revert changes in non CoreCLR specific code
MichalPetryka Nov 10, 2022
28561ac
Update src/libraries/System.Text.Json/src/System/Text/Json/Serializat…
jkotas Nov 10, 2022
9f108e9
Update src/libraries/System.Private.CoreLib/src/System/Enum.cs
jkotas Nov 10, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
14 changes: 12 additions & 2 deletions src/coreclr/inc/corinfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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(
Expand Down
4 changes: 4 additions & 0 deletions src/coreclr/inc/icorjitinfoimpl_generated.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
10 changes: 5 additions & 5 deletions src/coreclr/inc/jiteeversionguid.h
Original file line number Diff line number Diff line change
Expand Up @@ -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}
};

//////////////////////////////////////////////////////////////////////////////////////////////////////////
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/jit/ICorJitInfo_names_generated.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
10 changes: 10 additions & 0 deletions src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -2872,6 +2872,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);

Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/jit/fgbasic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
36 changes: 36 additions & 0 deletions src/coreclr/jit/gentree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18272,6 +18272,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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like you should try and leverage gtGetTypeProducerKind and/or gtIsTypeHandleToRuntimeTypeHelper here.

Copy link
Contributor Author

@MichalPetryka MichalPetryka Jul 10, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gtGetTypeProducerKind doesn't seem particularly useful here since we don't know the exact type with .GetType() and we don't want to return null types here so I've used gtIsTypeHandleToRuntimeTypeHelper and added tests for NRE with Type.GetTypeFromHandle(default) (which I assume generates the _MAYBENULL helper variant)(actually only __reftype seems to generate it).

{
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;
MichalPetryka marked this conversation as resolved.
Show resolved Hide resolved
}

//------------------------------------------------------------------------
// gtCallGetDefinedRetBufLclAddr:
// Get the tree corresponding to the address of the retbuf that this call defines.
Expand Down
52 changes: 17 additions & 35 deletions src/coreclr/jit/importer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -13402,15 +13389,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;
Expand Down
77 changes: 57 additions & 20 deletions src/coreclr/jit/importercalls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -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:
{
Expand All @@ -2865,35 +2866,56 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis,
// to `true` or `false`
// e.g., `typeof(int).IsValueType` => `true`
// e.g., `typeof(Span<int>).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))
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
{
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/jit/morph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11243,7 +11243,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");
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/jit/namedintrinsiclist.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Loading