Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit b334a7f

Browse files
committed
Remove call to canInline
Instead check for version resilience over on the VM side, and return a null handle if the method can't be devirtualized because it would build in a fragile dependence.
1 parent d543c16 commit b334a7f

File tree

2 files changed

+93
-76
lines changed

2 files changed

+93
-76
lines changed

src/jit/importer.cpp

Lines changed: 49 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -18480,13 +18480,12 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call,
1848018480
// Fetch the method that would be called based on the declared type of 'this'
1848118481
CORINFO_METHOD_HANDLE derivedMethod = info.compCompHnd->resolveVirtualMethod(baseMethod, objClass);
1848218482

18483-
// Bail if we can't get method info for the derived method
18484-
CORINFO_METHOD_INFO methInfo;
18485-
const bool hasInfo = info.compCompHnd->getMethodInfo(derivedMethod, &methInfo);
18486-
18487-
if (!hasInfo)
18483+
// If we failed to get a handle, we can't devirtualize. This can
18484+
// happen when prejitting, if the devirtualization crosses
18485+
// servicing bubble boundaries.
18486+
if (derivedMethod == nullptr)
1848818487
{
18489-
JITDUMP("--- no method info, sorry\n");
18488+
JITDUMP("--- no derived method, sorry\n");
1849018489
return;
1849118490
}
1849218491

@@ -18507,88 +18506,66 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call,
1850718506
}
1850818507
#endif // defined(DEBUG)
1850918508

18510-
// Verify we can inline -- both because we'll want to try and
18511-
// inline if we devirtualize, and also because this appropriately
18512-
// safeguards servicing bubbles.
18513-
//
18514-
// Note we may want to devirtualize even if we can't inline, but
18515-
// unfortunately today we can't distinguish between inlines that
18516-
// are marked noinline for perf reasons versus non-perf reasons.
18517-
// So we have to assume the worst.
18518-
//
18519-
// For instance when crossgenning corelib, we initially allow
18520-
// devirt of StringBuilder.ToString, but once we generate code for
18521-
// it we think it is a poor inline candidate, and mark it as
18522-
// noinline, and after that we refuse to devirtualize calls.
18523-
DWORD restrictions = 0;
18524-
CorInfoInline vmResult = info.compCompHnd->canInline(info.compMethodHnd, derivedMethod, &restrictions);
18525-
18526-
if ((vmResult == INLINE_PASS) && (restrictions == 0))
18509+
if (objClassIsFinal || derivedMethodIsFinal)
1852718510
{
18528-
if (objClassIsFinal || derivedMethodIsFinal)
18529-
{
18530-
JITDUMP("!!! Inlining ok, no restrictions, final; can devirtualize\n");
18511+
JITDUMP("!!! Inlining ok, no restrictions, final; can devirtualize\n");
18512+
printf("!!! Inlining ok, no restrictions, final; can devirtualize\n");
1853118513

18532-
// Make the updates.
18533-
call->gtFlags &= ~GTF_CALL_VIRT_VTABLE;
18534-
call->gtFlags &= ~GTF_CALL_VIRT_STUB;
18535-
call->gtCallMethHnd = derivedMethod;
18514+
// Make the updates.
18515+
call->gtFlags &= ~GTF_CALL_VIRT_VTABLE;
18516+
call->gtFlags &= ~GTF_CALL_VIRT_STUB;
18517+
call->gtCallMethHnd = derivedMethod;
1853618518

18537-
// Need to update call info too. This is fragile
18538-
// but hopefully the derived method conforms to
18539-
// the base in most other ways.
18540-
callInfo->hMethod = derivedMethod;
18541-
callInfo->methodFlags = derivedMethodAttribs;
18519+
// Need to update call info too. This is fragile
18520+
// but hopefully the derived method conforms to
18521+
// the base in most other ways.
18522+
callInfo->hMethod = derivedMethod;
18523+
callInfo->methodFlags = derivedMethodAttribs;
1854218524

18543-
// Clear the inline candidate info (may be non-null since
18544-
// it's a union field used for other things by virtual
18545-
// stubs)
18546-
call->gtInlineCandidateInfo = nullptr;
18525+
// Clear the inline candidate info (may be non-null since
18526+
// it's a union field used for other things by virtual
18527+
// stubs)
18528+
call->gtInlineCandidateInfo = nullptr;
1854718529

1854818530
#ifdef FEATURE_READYTORUN_COMPILER
18549-
if (opts.IsReadyToRun())
18550-
{
18551-
// We should refetch the lookup value from the VM.
18552-
call->setEntryPoint(callInfo->codePointerLookup.constLookup);
18553-
call->gtCallMoreFlags &= ~GTF_CALL_M_VIRTSTUB_REL_INDIRECT;
18554-
}
18531+
if (opts.IsReadyToRun())
18532+
{
18533+
// We should refetch the lookup value from the VM.
18534+
call->setEntryPoint(callInfo->codePointerLookup.constLookup);
18535+
call->gtCallMoreFlags &= ~GTF_CALL_M_VIRTSTUB_REL_INDIRECT;
18536+
}
1855518537
#endif
1855618538

18557-
// Note this may not equal objClass, if there is a
18558-
// final method that objClass inherits.
18559-
CORINFO_CLASS_HANDLE derivedClass = info.compCompHnd->getMethodClass(derivedMethod);
18539+
// Note this may not equal objClass, if there is a
18540+
// final method that objClass inherits.
18541+
CORINFO_CLASS_HANDLE derivedClass = info.compCompHnd->getMethodClass(derivedMethod);
1856018542

18561-
// Really should just re-invoke getCallInfo...
18562-
callInfo->contextHandle = MAKE_CLASSCONTEXT(derivedClass);
18543+
// Really should just re-invoke getCallInfo...
18544+
callInfo->contextHandle = MAKE_CLASSCONTEXT(derivedClass);
1856318545

18564-
// Update context handle too...
18565-
*exactContextHandle = MAKE_CLASSCONTEXT(derivedClass);
18546+
// Update context handle too...
18547+
*exactContextHandle = MAKE_CLASSCONTEXT(derivedClass);
1856618548

1856718549
#if defined(DEBUG)
18568-
if (verbose)
18569-
{
18570-
printf("... after devirt...\n");
18571-
gtDispTree(call);
18572-
}
18573-
#endif // defined(DEBUG)
18574-
}
18575-
else
18550+
if (verbose)
1857618551
{
18577-
// Neither class or method is final.
18578-
//
18579-
// We could speculatively devirtualize, but there's no
18580-
// reason to believe the derived method is the one that
18581-
// is likely to be invoked.
18582-
//
18583-
// If there's currently no further overriding (that is, at
18584-
// the time of jitting, objClass has no subclasses that
18585-
// override this method), then perhaps we'd be willing to
18586-
// make a bet...?
18587-
JITDUMP("??? Inlining ok, no restrictions, NOT final; speculative\n");
18552+
printf("... after devirt...\n");
18553+
gtDispTree(call);
1858818554
}
18555+
#endif // defined(DEBUG)
1858918556
}
1859018557
else
1859118558
{
18592-
JITDUMP("--- can't inline (%d) or has restrictions (%d), sorry\n", vmResult, restrictions);
18559+
// Neither class or method is final.
18560+
//
18561+
// We could speculatively devirtualize, but there's no
18562+
// reason to believe the derived method is the one that
18563+
// is likely to be invoked.
18564+
//
18565+
// If there's currently no further overriding (that is, at
18566+
// the time of jitting, objClass has no subclasses that
18567+
// override this method), then perhaps we'd be willing to
18568+
// make a bet...?
18569+
JITDUMP("??? NOT final; speculative devirt?\n");
1859318570
}
1859418571
}

src/vm/jitinterface.cpp

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8937,7 +8937,7 @@ CORINFO_METHOD_HANDLE CEEInfo::resolveVirtualMethodExact(CORINFO_METHOD_HANDLE m
89378937
MODE_PREEMPTIVE;
89388938
} CONTRACTL_END;
89398939

8940-
CORINFO_METHOD_HANDLE result;
8940+
CORINFO_METHOD_HANDLE result = nullptr;
89418941

89428942
JIT_TO_EE_TRANSITION();
89438943

@@ -8966,11 +8966,51 @@ CORINFO_METHOD_HANDLE CEEInfo::resolveVirtualMethodExact(CORINFO_METHOD_HANDLE m
89668966
// MethodDescs returned to JIT at runtime are always fully loaded. Verify that it is the case.
89678967
_ASSERTE(pMT->IsRestored() && pMT->IsFullyLoaded());
89688968

8969-
MethodDesc* pMD = pMT->GetMethodDescForSlot(slot);
8969+
MethodDesc* pDevirtMD = pMT->GetMethodDescForSlot(slot);
89708970

8971-
_ASSERTE(pMD->IsRestored());
8971+
_ASSERTE(pDevirtMD->IsRestored());
89728972

8973-
result = (CORINFO_METHOD_HANDLE) pMD;
8973+
// Allow devirtialization if jitting, or if prejitting and the
8974+
// method being jitted and the devirtualized method are in the
8975+
// same versioning bubble.
8976+
//
8977+
// Mirrors logic in canInline.
8978+
bool allowDevirt = false;
8979+
if (IsCompilingForNGen())
8980+
{
8981+
MethodDesc* pCallerMD = m_pMethodBeingCompiled;
8982+
Module* pCallerModule = pCallerMD->GetLoaderModule();
8983+
Module* pDevirtModule = pDevirtMD->GetModule();
8984+
Assembly* pCallerAssembly = pCallerModule->GetAssembly();
8985+
Assembly* pDevirtAssembly = pDevirtModule->GetAssembly();
8986+
8987+
if (pCallerAssembly == pDevirtAssembly)
8988+
{
8989+
// Ngen or R2R, same assembly
8990+
allowDevirt = true;
8991+
}
8992+
else
8993+
{
8994+
#ifdef FEATURE_READYTORUN_COMPILER
8995+
if (IsReadyToRunCompilation() &&
8996+
IsInSameVersionBubble(pCallerMD, pDevirtMD))
8997+
{
8998+
// R2R, same version bubble
8999+
allowDevirt = true;
9000+
}
9001+
#endif
9002+
}
9003+
}
9004+
else
9005+
{
9006+
// Jitting
9007+
allowDevirt = true;
9008+
}
9009+
9010+
if (allowDevirt)
9011+
{
9012+
result = (CORINFO_METHOD_HANDLE) pDevirtMD;
9013+
}
89749014

89759015
EE_TO_JIT_TRANSITION();
89769016

0 commit comments

Comments
 (0)