未验证 提交 0a351f53 编写于 作者: A Andy Ayers 提交者: GitHub

Refactor code that considers guarded devirtualization (#48611)

Merge two similar hunks of code.

Also, unblock guarded devirtualization when prejitting, as we may see class
guesses via static PGO.
上级 24f1acda
...@@ -6799,6 +6799,14 @@ public: ...@@ -6799,6 +6799,14 @@ public:
optMethodFlags &= ~OMF_HAS_GUARDEDDEVIRT; optMethodFlags &= ~OMF_HAS_GUARDEDDEVIRT;
} }
void considerGuardedDevirtualization(GenTreeCall* call,
IL_OFFSETX iloffset,
bool isInterface,
CORINFO_METHOD_HANDLE baseMethod,
CORINFO_CLASS_HANDLE baseClass,
CORINFO_CONTEXT_HANDLE* pContextHandle DEBUGARG(CORINFO_CLASS_HANDLE objClass)
DEBUGARG(const char* objClassName));
void addGuardedDevirtualizationCandidate(GenTreeCall* call, void addGuardedDevirtualizationCandidate(GenTreeCall* call,
CORINFO_METHOD_HANDLE methodHandle, CORINFO_METHOD_HANDLE methodHandle,
CORINFO_CLASS_HANDLE classHandle, CORINFO_CLASS_HANDLE classHandle,
......
...@@ -20975,65 +20975,8 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call, ...@@ -20975,65 +20975,8 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call,
return; return;
} }
JITDUMP("Considering guarded devirt (interface)...\n"); considerGuardedDevirtualization(call, ilOffset, isInterface, baseMethod, baseClass,
pContextHandle DEBUGARG(objClass) DEBUGARG(objClassName));
// See if the runtime can provide a class to guess for.
//
const unsigned interfaceLikelihoodThreshold = 25;
unsigned likelihood = 0;
unsigned numberOfClasses = 0;
CORINFO_CLASS_HANDLE likelyClass =
info.compCompHnd->getLikelyClass(info.compMethodHnd, baseClass, ilOffset, &likelihood, &numberOfClasses);
if (likelyClass == NO_CLASS_HANDLE)
{
JITDUMP("No likely implementor of interface %p (%s), sorry\n", dspPtr(objClass), objClassName);
return;
}
JITDUMP("Likely implementor of interface %p (%s) is %p (%s) [likelihood:%u classes seen:%u]\n",
dspPtr(objClass), objClassName, likelyClass, eeGetClassName(likelyClass), likelihood, numberOfClasses);
// Todo: a more advanced heuristic using likelihood, number of
// classes, and the profile count for this block.
//
// For now we will guess if the likelihood is 25% or more, as studies
// have shown this should pay off for interface calls.
//
if (likelihood < interfaceLikelihoodThreshold)
{
JITDUMP("Not guessing for class; likelihood is below interface call threshold %u\n",
interfaceLikelihoodThreshold);
return;
}
// Ask the runtime to determine the method that would be called based on the likely type.
//
CORINFO_DEVIRTUALIZATION_INFO dvInfo;
dvInfo.virtualMethod = baseMethod;
dvInfo.objClass = likelyClass;
dvInfo.context = *pContextHandle;
bool canResolve = info.compCompHnd->resolveVirtualMethod(&dvInfo);
if (!canResolve)
{
JITDUMP("Can't figure out which method would be invoked, sorry\n");
return;
}
CORINFO_METHOD_HANDLE likelyMethod = dvInfo.devirtualizedMethod;
JITDUMP("%s call would invoke method %s\n", callKind, eeGetMethodName(likelyMethod, nullptr));
// Some of these may be redundant
//
DWORD likelyMethodAttribs = info.compCompHnd->getMethodAttribs(likelyMethod);
DWORD likelyClassAttribs = info.compCompHnd->getClassAttribs(likelyClass);
// Try guarded devirtualization.
//
addGuardedDevirtualizationCandidate(call, likelyMethod, likelyClass, likelyMethodAttribs, likelyClassAttribs,
likelihood);
return; return;
} }
...@@ -21135,65 +21078,8 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call, ...@@ -21135,65 +21078,8 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call,
return; return;
} }
JITDUMP("Consdering guarded devirt (virtual)...\n"); considerGuardedDevirtualization(call, ilOffset, isInterface, baseMethod, baseClass,
pContextHandle DEBUGARG(objClass) DEBUGARG(objClassName));
// See if there's a likely guess for the class.
//
const unsigned likelihoodThreshold = isInterface ? 25 : 30;
unsigned likelihood = 0;
unsigned numberOfClasses = 0;
CORINFO_CLASS_HANDLE likelyClass =
info.compCompHnd->getLikelyClass(info.compMethodHnd, baseClass, ilOffset, &likelihood, &numberOfClasses);
if (likelyClass == NO_CLASS_HANDLE)
{
JITDUMP("No likely class, sorry\n");
return;
}
JITDUMP("Likely class for %p (%s) is %p (%s) [likelihood:%u classes seen:%u]\n", dspPtr(objClass), objClassName,
likelyClass, eeGetClassName(likelyClass), likelihood, numberOfClasses);
// Todo: a more advanced heuristic using likelihood, number of
// classes, and the profile count for this block.
//
// For now we will guess if the likelihood is at least 25%/30% (intfc/virt), as studies
// have shown this transformation should pay off even if we guess wrong sometimes.
//
if (likelihood < likelihoodThreshold)
{
JITDUMP("Not guessing for class; likelihood is below %s call threshold %u\n", callKind,
likelihoodThreshold);
return;
}
// Figure out which method will be called.
//
CORINFO_DEVIRTUALIZATION_INFO dvInfo;
dvInfo.virtualMethod = baseMethod;
dvInfo.objClass = likelyClass;
dvInfo.context = *pContextHandle;
bool canResolve = info.compCompHnd->resolveVirtualMethod(&dvInfo);
if (!canResolve)
{
JITDUMP("Can't figure out which method would be invoked, sorry\n");
return;
}
CORINFO_METHOD_HANDLE likelyMethod = dvInfo.devirtualizedMethod;
JITDUMP("%s call would invoke method %s\n", callKind, eeGetMethodName(likelyMethod, nullptr));
// Some of these may be redundant
//
DWORD likelyMethodAttribs = info.compCompHnd->getMethodAttribs(likelyMethod);
DWORD likelyClassAttribs = info.compCompHnd->getClassAttribs(likelyClass);
// Try guarded devirtualization.
//
addGuardedDevirtualizationCandidate(call, likelyMethod, likelyClass, likelyMethodAttribs, likelyClassAttribs,
likelihood);
return; return;
} }
...@@ -21584,25 +21470,109 @@ void Compiler::addFatPointerCandidate(GenTreeCall* call) ...@@ -21584,25 +21470,109 @@ void Compiler::addFatPointerCandidate(GenTreeCall* call)
helper.StoreRetExprResultsInArgs(call); helper.StoreRetExprResultsInArgs(call);
} }
//------------------------------------------------------------------------
// considerGuardedDevirtualization: see if we can profitably guess at the
// class involved in an interface or virtual call.
//
// Arguments:
//
// call - potential guarded devirtualization candidate
// ilOffset - IL offset of the call instruction
// isInterface - true if this is an interface call
// baseMethod - target method of the call
// baseClass - class that introduced the target method
// pContextHandle - context handle for the call
// objClass - class of 'this' in the call
// objClassName - name of the obj Class
//
// Notes:
// Consults with VM to see if there's a likely class at runtime,
// if so, adds a candidate for guarded devirtualization.
//
void Compiler::considerGuardedDevirtualization(
GenTreeCall* call,
IL_OFFSETX ilOffset,
bool isInterface,
CORINFO_METHOD_HANDLE baseMethod,
CORINFO_CLASS_HANDLE baseClass,
CORINFO_CONTEXT_HANDLE* pContextHandle DEBUGARG(CORINFO_CLASS_HANDLE objClass) DEBUGARG(const char* objClassName))
{
#if defined(DEBUG)
const char* callKind = isInterface ? "interface" : "virtual";
#endif
JITDUMP("Considering guarded devirtualization\n");
// See if there's a likely guess for the class.
//
const unsigned likelihoodThreshold = isInterface ? 25 : 30;
unsigned likelihood = 0;
unsigned numberOfClasses = 0;
CORINFO_CLASS_HANDLE likelyClass =
info.compCompHnd->getLikelyClass(info.compMethodHnd, baseClass, ilOffset, &likelihood, &numberOfClasses);
if (likelyClass == NO_CLASS_HANDLE)
{
JITDUMP("No likely class, sorry\n");
return;
}
JITDUMP("Likely class for %p (%s) is %p (%s) [likelihood:%u classes seen:%u]\n", dspPtr(objClass), objClassName,
likelyClass, eeGetClassName(likelyClass), likelihood, numberOfClasses);
// Todo: a more advanced heuristic using likelihood, number of
// classes, and the profile count for this block.
//
// For now we will guess if the likelihood is at least 25%/30% (intfc/virt), as studies
// have shown this transformation should pay off even if we guess wrong sometimes.
//
if (likelihood < likelihoodThreshold)
{
JITDUMP("Not guessing for class; likelihood is below %s call threshold %u\n", callKind, likelihoodThreshold);
return;
}
// Figure out which method will be called.
//
CORINFO_DEVIRTUALIZATION_INFO dvInfo;
dvInfo.virtualMethod = baseMethod;
dvInfo.objClass = likelyClass;
dvInfo.context = *pContextHandle;
const bool canResolve = info.compCompHnd->resolveVirtualMethod(&dvInfo);
if (!canResolve)
{
JITDUMP("Can't figure out which method would be invoked, sorry\n");
return;
}
CORINFO_METHOD_HANDLE likelyMethod = dvInfo.devirtualizedMethod;
JITDUMP("%s call would invoke method %s\n", callKind, eeGetMethodName(likelyMethod, nullptr));
// Add this as a potential candidate.
//
uint32_t const likelyMethodAttribs = info.compCompHnd->getMethodAttribs(likelyMethod);
uint32_t const likelyClassAttribs = info.compCompHnd->getClassAttribs(likelyClass);
addGuardedDevirtualizationCandidate(call, likelyMethod, likelyClass, likelyMethodAttribs, likelyClassAttribs,
likelihood);
}
//------------------------------------------------------------------------ //------------------------------------------------------------------------
// addGuardedDevirtualizationCandidate: potentially mark the call as a guarded // addGuardedDevirtualizationCandidate: potentially mark the call as a guarded
// devirtualization candidate // devirtualization candidate
// //
// Notes: // Notes:
// //
// We currently do not mark calls as candidates when prejitting. This was done
// to simplify bringing up the associated transformation. It is worth revisiting
// if we think we can come up with a good guess for the class when prejitting.
//
// Call sites in rare or unoptimized code, and calls that require cookies are // Call sites in rare or unoptimized code, and calls that require cookies are
// also not marked as candidates. // not marked as candidates.
// //
// As part of marking the candidate, the code spills GT_RET_EXPRs anywhere in any // As part of marking the candidate, the code spills GT_RET_EXPRs anywhere in any
// child tree, because and we need to clone all these trees when we clone the call // child tree, because and we need to clone all these trees when we clone the call
// as part of guarded devirtualization, and these IR nodes can't be cloned. // as part of guarded devirtualization, and these IR nodes can't be cloned.
// //
// Arguments: // Arguments:
// call - potentual guarded devirtialization candidate // call - potential guarded devirtualization candidate
// methodHandle - method that will be invoked if the class test succeeds // methodHandle - method that will be invoked if the class test succeeds
// classHandle - class that will be tested for at runtime // classHandle - class that will be tested for at runtime
// methodAttr - attributes of the method // methodAttr - attributes of the method
...@@ -21629,14 +21599,6 @@ void Compiler::addGuardedDevirtualizationCandidate(GenTreeCall* call, ...@@ -21629,14 +21599,6 @@ void Compiler::addGuardedDevirtualizationCandidate(GenTreeCall* call,
return; return;
} }
// Bail when prejitting. We only do this for jitted code.
// We shoud revisit this if we think we can come up with good class guesses when prejitting.
if (opts.jitFlags->IsSet(JitFlags::JIT_FLAG_PREJIT))
{
JITDUMP("NOT Marking call [%06u] as guarded devirtualization candidate -- prejitting", dspTreeID(call));
return;
}
// Bail if not optimizing or the call site is very likely cold // Bail if not optimizing or the call site is very likely cold
if (compCurBB->isRunRarely() || opts.OptimizationDisabled()) if (compCurBB->isRunRarely() || opts.OptimizationDisabled())
{ {
...@@ -21651,6 +21613,8 @@ void Compiler::addGuardedDevirtualizationCandidate(GenTreeCall* call, ...@@ -21651,6 +21613,8 @@ void Compiler::addGuardedDevirtualizationCandidate(GenTreeCall* call,
// we save the stub address below. // we save the stub address below.
if ((call->gtCallType == CT_INDIRECT) && (call->AsCall()->gtCallCookie != nullptr)) if ((call->gtCallType == CT_INDIRECT) && (call->AsCall()->gtCallCookie != nullptr))
{ {
JITDUMP("NOT Marking call [%06u] as guarded devirtualization candidate -- CT_INDIRECT with cookie\n",
dspTreeID(call));
return; return;
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册