-
Notifications
You must be signed in to change notification settings - Fork 5.5k
Support devirtualization for virtual methods that require runtime lookups #129806
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
base: main
Are you sure you want to change the base?
Changes from all commits
2723cdd
9ce2f81
2f787b6
071523a
309d8fd
7721087
0dc166e
7de7f0c
058644a
fd30d8d
3704f25
ecb5475
218669d
e93af8c
ab94545
fb42135
a9780b4
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 |
|---|---|---|
|
|
@@ -1086,7 +1086,7 @@ var_types Compiler::impImportCall(OPCODE opcode, | |
| else if (call->AsCall()->IsDelegateInvoke()) | ||
| { | ||
| considerGuardedDevirtualization(call->AsCall(), rawILOffset, false, call->AsCall()->gtCallMethHnd, | ||
| NO_CLASS_HANDLE, nullptr); | ||
| NO_CLASS_HANDLE, nullptr, pResolvedToken); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -1367,6 +1367,7 @@ var_types Compiler::impImportCall(OPCODE opcode, | |
| info->methodHnd = callInfo->hMethod; | ||
| info->exactContextHnd = exactContextHnd; | ||
| info->ilLocation = impCurStmtDI.GetLocation(); | ||
| info->resolvedToken = *pResolvedToken; | ||
| call->AsCall()->gtLateDevirtualizationInfo = info; | ||
| } | ||
| } | ||
|
|
@@ -8013,7 +8014,8 @@ void Compiler::considerGuardedDevirtualization(GenTreeCall* call, | |
| bool isInterface, | ||
| CORINFO_METHOD_HANDLE baseMethod, | ||
| CORINFO_CLASS_HANDLE baseClass, | ||
| CORINFO_CONTEXT_HANDLE* pContextHandle) | ||
| CORINFO_CONTEXT_HANDLE* pContextHandle, | ||
| CORINFO_RESOLVED_TOKEN* pResolvedToken) | ||
| { | ||
| JITDUMP("Considering guarded devirtualization at IL offset %u (0x%x)\n", ilOffset, ilOffset); | ||
|
|
||
|
|
@@ -8092,9 +8094,10 @@ void Compiler::considerGuardedDevirtualization(GenTreeCall* call, | |
| // | ||
| CORINFO_DEVIRTUALIZATION_INFO dvInfo; | ||
| dvInfo.virtualMethod = baseMethod; | ||
| dvInfo.callerMethod = call->gtInlineContext->GetCallee(); | ||
| dvInfo.objClass = exactCls; | ||
| dvInfo.context = originalContext; | ||
| dvInfo.pResolvedTokenVirtualMethod = nullptr; | ||
| dvInfo.pResolvedTokenVirtualMethod = pResolvedToken; | ||
|
|
||
| JITDUMP("GDV exact: resolveVirtualMethod (method %p class %p context %p)\n", dvInfo.virtualMethod, | ||
| dvInfo.objClass, dvInfo.context); | ||
|
|
@@ -8177,6 +8180,7 @@ void Compiler::considerGuardedDevirtualization(GenTreeCall* call, | |
| // Figure out which method will be called. | ||
| // | ||
| dvInfo.virtualMethod = baseMethod; | ||
| dvInfo.callerMethod = call->gtInlineContext->GetCallee(); | ||
| dvInfo.objClass = likelyClass; | ||
| dvInfo.context = originalContext; | ||
| dvInfo.pResolvedTokenVirtualMethod = nullptr; | ||
|
|
@@ -9229,7 +9233,8 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call, | |
| return; | ||
| } | ||
|
|
||
| considerGuardedDevirtualization(call, ilOffset, isInterface, baseMethod, baseClass, pContextHandle); | ||
| considerGuardedDevirtualization(call, ilOffset, isInterface, baseMethod, baseClass, pContextHandle, | ||
| pResolvedToken); | ||
|
|
||
| return; | ||
| } | ||
|
|
@@ -9273,7 +9278,8 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call, | |
| return; | ||
| } | ||
|
|
||
| considerGuardedDevirtualization(call, ilOffset, isInterface, baseMethod, baseClass, pContextHandle); | ||
| considerGuardedDevirtualization(call, ilOffset, isInterface, baseMethod, baseClass, pContextHandle, | ||
| pResolvedToken); | ||
| return; | ||
| } | ||
|
|
||
|
|
@@ -9285,11 +9291,9 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call, | |
| JITDUMP("--- base class is interface\n"); | ||
| } | ||
|
|
||
| // Fetch the method that would be called based on the declared type of 'this', | ||
| // and prepare to fetch the method attributes. | ||
| // | ||
| CORINFO_DEVIRTUALIZATION_INFO dvInfo; | ||
| dvInfo.virtualMethod = baseMethod; | ||
| dvInfo.callerMethod = call->gtInlineContext->GetCallee(); | ||
| dvInfo.objClass = objClass; | ||
| dvInfo.context = *pContextHandle; | ||
| dvInfo.detail = CORINFO_DEVIRTUALIZATION_UNKNOWN; | ||
|
|
@@ -9300,6 +9304,56 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call, | |
|
|
||
| info.compCompHnd->resolveVirtualMethod(&dvInfo); | ||
|
|
||
| GenTree* runtimeLookupContext = nullptr; | ||
|
|
||
| if (isLateDevirtualization && dvInfo.instParamLookup.lookupKind.needsRuntimeLookup) | ||
| { | ||
| // Late devirtualization may revisit a call that was imported in an inlinee. | ||
| // If token context is not the current root context, runtime lookups can be rooted in the wrong generic context. | ||
| // | ||
| CORINFO_CONTEXT_HANDLE tokenContext = pResolvedToken->tokenContext; | ||
| const SIZE_T tokenContextHandle = (SIZE_T)tokenContext & ~CORINFO_CONTEXTFLAGS_MASK; | ||
|
|
||
| const bool isMethodContext = ((SIZE_T)tokenContext & CORINFO_CONTEXTFLAGS_MASK) == CORINFO_CONTEXTFLAGS_METHOD; | ||
| const bool isCurrentContext = | ||
| (tokenContext == METHOD_BEING_COMPILED_CONTEXT()) || | ||
| (isMethodContext ? ((CORINFO_METHOD_HANDLE)tokenContextHandle == info.compMethodHnd) | ||
| : ((CORINFO_CLASS_HANDLE)tokenContextHandle == info.compClassHnd)); | ||
|
|
||
| // If we don't have the right context, try recover it from the tree. | ||
| // | ||
| if (!isCurrentContext) | ||
| { | ||
| CallArg* runtimeMethodHandleArg = nullptr; | ||
| if ((call->gtControlExpr != nullptr) && call->gtControlExpr->OperIs(GT_CALL)) | ||
| { | ||
| runtimeMethodHandleArg = | ||
| call->gtControlExpr->AsCall()->gtArgs.FindWellKnownArg(WellKnownArg::RuntimeMethodHandle); | ||
| } | ||
|
|
||
| if (runtimeMethodHandleArg != nullptr && runtimeMethodHandleArg->GetNode()->OperIs(GT_RUNTIMELOOKUP)) | ||
| { | ||
| if (runtimeMethodHandleArg->GetNode()->AsRuntimeLookup()->Lookup()->OperIs(GT_CALL)) | ||
| { | ||
| GenTreeCall* const helperCall = | ||
| runtimeMethodHandleArg->GetNode()->AsRuntimeLookup()->Lookup()->AsCall(); | ||
|
|
||
| if (helperCall->IsHelperCall(CORINFO_HELP_RUNTIMEHANDLE_METHOD) || | ||
| helperCall->IsHelperCall(CORINFO_HELP_RUNTIMEHANDLE_CLASS)) | ||
| { | ||
| runtimeLookupContext = helperCall->gtArgs.GetArgByIndex(0)->GetNode(); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| if (runtimeLookupContext == nullptr) | ||
| { | ||
| JITDUMP("Late devirt needs a runtime lookup context cannot be figured out. Bail out.\n"); | ||
| return; | ||
| } | ||
| } | ||
|
Comment on lines
+9323
to
+9354
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. Why can this not be done where it is needed? Trying to smuggle IR through across phases without actually adding it does not scale well. I do not think this is a workable way of doing this.
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. Ah,
Contributor
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.
Alternatively we will need to add something to |
||
| } | ||
|
|
||
| CORINFO_METHOD_HANDLE derivedMethod = dvInfo.devirtualizedMethod; | ||
| CORINFO_CONTEXT_HANDLE exactContext = dvInfo.tokenLookupContext; | ||
| CORINFO_RESOLVED_TOKEN* pDerivedResolvedToken = &dvInfo.resolvedTokenDevirtualizedMethod; | ||
|
|
@@ -9395,7 +9449,8 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call, | |
| return; | ||
| } | ||
|
|
||
| considerGuardedDevirtualization(call, ilOffset, isInterface, baseMethod, objClass, pContextHandle); | ||
| considerGuardedDevirtualization(call, ilOffset, isInterface, baseMethod, objClass, pContextHandle, | ||
| pResolvedToken); | ||
| return; | ||
| } | ||
|
|
||
|
|
@@ -9410,6 +9465,7 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call, | |
| dcInfo.pInstParamLookup = &dvInfo.instParamLookup; | ||
| dcInfo.pResolvedToken = pDerivedResolvedToken; | ||
| dcInfo.pUnboxedResolvedToken = &dvInfo.resolvedTokenDevirtualizedUnboxedMethod; | ||
| dcInfo.runtimeLookupContext = runtimeLookupContext; | ||
| dcInfo.pMethSig = &derivedSig; | ||
| dcInfo.objIsNonNull = objIsNonNull; | ||
| dcInfo.hadImplicitNullCheck = true; | ||
|
|
@@ -9628,7 +9684,8 @@ void Compiler::impTransformDevirtualizedCall(GenTreeCall* call, | |
| CORINFO_METHOD_HANDLE exactMethodHandle = | ||
| (CORINFO_METHOD_HANDLE)((SIZE_T)dcInfo->tokenLookupContext & ~CORINFO_CONTEXTFLAGS_MASK); | ||
|
|
||
| instParam = getLookupTree(dcInfo->pInstParamLookup, GTF_ICON_METHOD_HDL, exactMethodHandle); | ||
| instParam = getLookupTree(dcInfo->pInstParamLookup, GTF_ICON_METHOD_HDL, exactMethodHandle, | ||
| dcInfo->runtimeLookupContext); | ||
| JITDUMP("revising call to invoke unboxed entry with additional method desc arg\n"); | ||
| } | ||
| else | ||
|
|
@@ -9710,7 +9767,8 @@ void Compiler::impTransformDevirtualizedCall(GenTreeCall* call, | |
| CORINFO_METHOD_HANDLE exactMethodHandle = | ||
| (CORINFO_METHOD_HANDLE)((SIZE_T)dcInfo->tokenLookupContext & ~CORINFO_CONTEXTFLAGS_MASK); | ||
|
|
||
| instParam = getLookupTree(dcInfo->pInstParamLookup, GTF_ICON_METHOD_HDL, exactMethodHandle); | ||
| instParam = getLookupTree(dcInfo->pInstParamLookup, GTF_ICON_METHOD_HDL, exactMethodHandle, | ||
| dcInfo->runtimeLookupContext); | ||
| } | ||
| else | ||
| { | ||
|
|
@@ -9719,7 +9777,8 @@ void Compiler::impTransformDevirtualizedCall(GenTreeCall* call, | |
| CORINFO_CLASS_HANDLE exactClassHandle = | ||
| (CORINFO_CLASS_HANDLE)((SIZE_T)dcInfo->tokenLookupContext & ~CORINFO_CONTEXTFLAGS_MASK); | ||
|
|
||
| instParam = getLookupTree(dcInfo->pInstParamLookup, GTF_ICON_CLASS_HDL, exactClassHandle); | ||
| instParam = getLookupTree(dcInfo->pInstParamLookup, GTF_ICON_CLASS_HDL, exactClassHandle, | ||
| dcInfo->runtimeLookupContext); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -634,6 +634,7 @@ struct LateDevirtualizationInfo | |
| CORINFO_METHOD_HANDLE methodHnd; | ||
| CORINFO_CONTEXT_HANDLE exactContextHnd; | ||
| ILLocation ilLocation; | ||
| CORINFO_RESOLVED_TOKEN resolvedToken; | ||
|
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. Resolved tokens are large structures. Does this show up on memory use?
Contributor
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. I didn't measure it yet, but it is necessary for computing the runtime lookup. I can't think of a better way than disabling late devirtualization for this if we don't want to store a resolved token. |
||
| }; | ||
|
|
||
| // InlArgInfo describes inline candidate argument properties. | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.