-
Notifications
You must be signed in to change notification settings - Fork 5.3k
JIT: Fold typeof(T).TypeHandle.Value #85804
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
Changes from all commits
52065e1
04210ec
3c81b79
e4d391b
375df02
9cd3880
7716934
726a42f
5b313af
5bdfc2c
7821c85
7ab11e5
ec522f2
83a6fae
473a361
eaeaef5
0442988
0e4b625
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -225,7 +225,8 @@ var_types Compiler::impImportCall(OPCODE opcode, | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const bool isTailCall = canTailCall && (tailCallFlags != 0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| call = impIntrinsic(newobjThis, clsHnd, methHnd, sig, mflags, pResolvedToken, isReadonlyCall, isTailCall, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pConstrainedResolvedToken, callInfo->thisTransform, &ni, &isSpecialIntrinsic); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| opcode == CEE_CALLVIRT, pConstrainedResolvedToken, callInfo->thisTransform, &ni, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| &isSpecialIntrinsic); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (compDonotInline()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -2288,6 +2289,7 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| CORINFO_RESOLVED_TOKEN* pResolvedToken, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| bool readonlyCall, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| bool tailCall, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| bool callvirt, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| CORINFO_RESOLVED_TOKEN* pConstrainedResolvedToken, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| CORINFO_THIS_TRANSFORM constraintCallThisTransform, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| NamedIntrinsic* pIntrinsicName, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -2574,7 +2576,6 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case NI_System_Runtime_CompilerServices_RuntimeHelpers_IsKnownConstant: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // We need these to be able to fold "typeof(...) == typeof(...)" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case NI_System_RuntimeTypeHandle_ToIntPtr: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case NI_System_Type_GetTypeFromHandle: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case NI_System_Type_op_Equality: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case NI_System_Type_op_Inequality: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -2599,6 +2600,9 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case NI_System_BitConverter_SingleToInt32Bits: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case NI_System_Buffers_Binary_BinaryPrimitives_ReverseEndianness: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case NI_System_Type_GetEnumUnderlyingType: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case NI_System_Type_get_TypeHandle: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case NI_System_RuntimeType_get_TypeHandle: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case NI_System_RuntimeTypeHandle_ToIntPtr: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Most atomics are compiled to single instructions | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case NI_System_Threading_Interlocked_And: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -2955,8 +2959,8 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case NI_System_RuntimeTypeHandle_ToIntPtr: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| GenTree* op1 = impStackTop(0).val; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (op1->gtOper == GT_CALL && (op1->AsCall()->gtCallType == CT_HELPER) && | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| gtIsTypeHandleToRuntimeTypeHandleHelper(op1->AsCall())) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (op1->IsHelperCall() && gtIsTypeHandleToRuntimeTypeHandleHelper(op1->AsCall())) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Old tree | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Helper-RuntimeTypeHandle -> TreeToGetNativeTypeHandle | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -2974,7 +2978,28 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| op1 = op1->AsCall()->gtArgs.GetArgByIndex(0)->GetNode(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| retNode = op1; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Call the regular function. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| else if (op1->OperIs(GT_CALL, GT_RET_EXPR)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Skip roundtrip "handle -> RuntimeType -> handle" for | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // RuntimeTypeHandle.ToIntPtr(typeof(T).TypeHandle) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| GenTreeCall* call = op1->IsCall() ? op1->AsCall() : op1->AsRetExpr()->gtInlineCandidate; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (lookupNamedIntrinsic(call->gtCallMethHnd) == NI_System_RuntimeType_get_TypeHandle) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Check that the arg is CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE helper call | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| GenTree* arg = call->gtArgs.GetArgByIndex(0)->GetNode(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (arg->IsHelperCall() && gtIsTypeHandleToRuntimeTypeHelper(arg->AsCall())) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| impPopStack(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (op1->OperIs(GT_RET_EXPR)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Bash the RET_EXPR's call to no-op since it's unused now | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| op1->AsRetExpr()->gtInlineCandidate->gtBashToNOP(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Skip roundtrip and return the type handle directly | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| retNode = arg->AsCall()->gtArgs.GetArgByIndex(0)->GetNode(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -3074,6 +3099,30 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case NI_System_Type_get_TypeHandle: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // We can only expand this on NativeAOT where RuntimeTypeHandle looks like this: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // struct RuntimeTypeHandle { IntPtr _value; } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| GenTree* op1 = impStackTop(0).val; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (IsTargetAbi(CORINFO_NATIVEAOT_ABI) && op1->IsHelperCall() && | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| gtIsTypeHandleToRuntimeTypeHelper(op1->AsCall()) && callvirt) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert(info.compCompHnd->getClassNumInstanceFields(sig->retTypeClass) == 1); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| unsigned structLcl = lvaGrabTemp(true DEBUGARG("RuntimeTypeHandle")); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| lvaSetStruct(structLcl, sig->retTypeClass, false); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| GenTree* realHandle = op1->AsCall()->gtArgs.GetUserArgByIndex(0)->GetNode(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| GenTreeLclFld* handleFld = gtNewLclFldNode(structLcl, realHandle->TypeGet(), 0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| GenTree* asgHandleFld = gtNewAssignNode(handleFld, realHandle); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it be better to create this as We have other places in the JIT that look for
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It probably makes sense but we'll need to fold it back to a constant somewhere later, I tried to do this and hit no diffs (I'm running ILC for a complex app) so maybe we better teach jit to recognize raw handles if we ever find a use case? Presumably, there is nothing JIT can do additionally for
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The canonical shape for creating RuntimeTypeHandle from raw handle used in other places is Cleaning up
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here is the change you wanted to see EgorBo@55279bc It seems to be not needed for CoreCLR since we have to wrap RuntimeType object and I've ran SPMI searching for I spent some time trying to find a case where this helps and wasn't able to do so, I only see a worse codegen (a stack spill I can't explain) - my understanding that we promote structs right after importer so it's better to expand them there.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe that the right way to do this optimization is without any AOT special-casing and without @dotnet/jit-contrib Any opinions about this?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right, I just feel that the energy is better to spent elsewhere since:
So we only miss the shared generic case on CoreCLR and unloadable ALC (on CoreCLR). To support both of them we need to mark two methods with [Intrinsic] (
We can sort of do this after importer and ForwardSub but we'll need to disable inlining for them which is not great either.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
If it helps, I would be ok with ignoring
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes, that one is at least do-able in importer, I've just added it
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is some special spill avoidance in the importer to make recognizing these kinds of multi-call patterns easier; you could extend this if needed. runtime/src/coreclr/jit/importercalls.cpp Lines 1414 to 1442 in 7d54c05
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| impAppendTree(asgHandleFld, CHECK_SPILL_NONE, impCurStmtDI); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| retNode = impCreateLocalNode(structLcl DEBUGARG(0)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| impPopStack(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case NI_System_Type_get_IsEnum: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case NI_System_Type_get_IsValueType: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case NI_System_Type_get_IsByRefLike: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -8122,6 +8171,10 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result = NI_System_Type_get_IsEnum; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (strcmp(methodName, "get_TypeHandle") == 0) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result = NI_System_RuntimeType_get_TypeHandle; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| else if (strcmp(className, "RuntimeTypeHandle") == 0) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -8219,6 +8272,10 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result = NI_System_Type_op_Inequality; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| else if (strcmp(methodName, "get_TypeHandle") == 0) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result = NI_System_Type_get_TypeHandle; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||

Uh oh!
There was an error while loading. Please reload this page.