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

Simple devirtualization #9230

Merged
merged 2 commits into from
Mar 2, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 7 additions & 0 deletions src/ToolBox/superpmi/superpmi-shared/icorjitinfoimpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,13 @@
unsigned* offsetAfterIndirection /* OUT */
);

// Find the virtual method in implementingClass that overrides virtualMethod.
// Return null if devirtualization is not possible.
CORINFO_METHOD_HANDLE resolveVirtualMethod(
CORINFO_METHOD_HANDLE virtualMethod,
CORINFO_CLASS_HANDLE implementingClass
);

// If a method's attributes have (getMethodAttribs) CORINFO_FLG_INTRINSIC set,
// getIntrinsicID() returns the intrinsic ID.
// *pMustExpand tells whether or not JIT must expand the intrinsic.
Expand Down
1 change: 1 addition & 0 deletions src/ToolBox/superpmi/superpmi-shared/lwmlist.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ LWM(IsWriteBarrierHelperRequired, DWORDLONG, DWORD)
LWM(MergeClasses, DLDL, DWORDLONG)
LWM(PInvokeMarshalingRequired, Agnostic_PInvokeMarshalingRequired, DWORD)
LWM(ResolveToken, Agnostic_CORINFO_RESOLVED_TOKENin, Agnostic_CORINFO_RESOLVED_TOKENout)
LWM(ResolveVirtualMethod, DLDL, DWORDLONG)
LWM(TryResolveToken, Agnostic_CORINFO_RESOLVED_TOKENin, Agnostic_CORINFO_RESOLVED_TOKENout)
LWM(SatisfiesClassConstraints, DWORDLONG, DWORD)
LWM(SatisfiesMethodConstraints, DLDL, DWORD)
Expand Down
36 changes: 35 additions & 1 deletion src/ToolBox/superpmi/superpmi-shared/methodcontext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3296,6 +3296,40 @@ void MethodContext::repGetMethodVTableOffset(CORINFO_METHOD_HANDLE method, unsig
DEBUG_REP(dmpGetMethodVTableOffset((DWORDLONG)method, value));
}

void MethodContext::recResolveVirtualMethod(CORINFO_METHOD_HANDLE virtMethod, CORINFO_CLASS_HANDLE implClass, CORINFO_METHOD_HANDLE result)
{
if (ResolveVirtualMethod == nullptr)
{
ResolveVirtualMethod = new LightWeightMap<DLDL, DWORDLONG>();
}

DLDL key;
key.A = (DWORDLONG)virtMethod;
key.B = (DWORDLONG)implClass;
ResolveVirtualMethod->Add(key, (DWORDLONG) result);
DEBUG_REC(dmpResolveVirtualMethod(key, result));
}

void MethodContext::dmpResolveVirtualMethod(DLDL key, DWORDLONG value)
{
printf("ResolveVirtualMethod virtMethod-%016llX, implClass-%016llX, result-%016llX", key.A, key.B, value);
}

CORINFO_METHOD_HANDLE MethodContext::repResolveVirtualMethod(CORINFO_METHOD_HANDLE virtMethod, CORINFO_CLASS_HANDLE implClass)
{
DLDL key;
key.A = (DWORDLONG)virtMethod;
key.B = (DWORDLONG)implClass;

AssertCodeMsg(ResolveVirtualMethod != nullptr, EXCEPTIONCODE_MC, "No ResolveVirtualMap map for %016llX-%016llX", key.A, key.B);
AssertCodeMsg(ResolveVirtualMethod->GetIndex(key) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX-%016llx", key.A, key.B);
DWORDLONG result = ResolveVirtualMethod->Get(key);

DEBUG_REP(dmpResolveVirtualMethod(key, result));

return (CORINFO_METHOD_HANDLE)result;
}

void MethodContext::recGetTokenTypeAsHandle(CORINFO_RESOLVED_TOKEN * pResolvedToken, CORINFO_CLASS_HANDLE result)
{
if (GetTokenTypeAsHandle == nullptr)
Expand Down Expand Up @@ -6165,7 +6199,7 @@ mdMethodDef MethodContext::repGetMethodDefFromMethod(CORINFO_METHOD_HANDLE hMeth

int index = GetMethodDefFromMethod->GetIndex((DWORDLONG)hMethod);
if (index < 0)
return (mdMethodDef)0x06000001;
return (mdMethodDef)0x06000001;

return (mdMethodDef)GetMethodDefFromMethod->Get((DWORDLONG)hMethod);
}
Expand Down
7 changes: 6 additions & 1 deletion src/ToolBox/superpmi/superpmi-shared/methodcontext.h
Original file line number Diff line number Diff line change
Expand Up @@ -698,6 +698,10 @@ class MethodContext
void dmpGetMethodVTableOffset(DWORDLONG key, DD value);
void repGetMethodVTableOffset(CORINFO_METHOD_HANDLE method, unsigned *offsetOfIndirection, unsigned* offsetAfterIndirection);

void recResolveVirtualMethod(CORINFO_METHOD_HANDLE virtMethod, CORINFO_CLASS_HANDLE implClass, CORINFO_METHOD_HANDLE result);
void dmpResolveVirtualMethod(DLDL key, DWORDLONG value);
CORINFO_METHOD_HANDLE repResolveVirtualMethod(CORINFO_METHOD_HANDLE virtMethod, CORINFO_CLASS_HANDLE implClass);

void recGetTokenTypeAsHandle(CORINFO_RESOLVED_TOKEN * pResolvedToken, CORINFO_CLASS_HANDLE result);
void dmpGetTokenTypeAsHandle(const Agnostic_CORINFO_RESOLVED_TOKEN& key, DWORDLONG value);
CORINFO_CLASS_HANDLE repGetTokenTypeAsHandle(CORINFO_RESOLVED_TOKEN * pResolvedToken);
Expand Down Expand Up @@ -1016,7 +1020,7 @@ class MethodContext


// ********************* Please keep this up-to-date to ease adding more ***************
// Highest packet number: 159
// Highest packet number: 160
// *************************************************************************************
enum mcPackets
{
Expand Down Expand Up @@ -1151,6 +1155,7 @@ enum mcPackets
Packet_MergeClasses = 107,
Packet_PInvokeMarshalingRequired = 108,
Packet_ResolveToken = 109,
Packet_ResolveVirtualMethod = 160, // Added 2/13/17
Packet_TryResolveToken = 158, //Added 4/26/2016
Packet_SatisfiesClassConstraints = 110,
Packet_SatisfiesMethodConstraints = 111,
Expand Down
13 changes: 13 additions & 0 deletions src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,19 @@ void interceptor_ICJI::getMethodVTableOffset (
mc->recGetMethodVTableOffset(method, offsetOfIndirection, offsetAfterIndirection);
}

// Find the virtual method in implementingClass that overrides virtualMethod.
// Return null if devirtualization is not possible.
CORINFO_METHOD_HANDLE interceptor_ICJI::resolveVirtualMethod(
CORINFO_METHOD_HANDLE virtualMethod,
CORINFO_CLASS_HANDLE implementingClass
)
{
mc->cr->AddCall("resolveVirtualMethod");
CORINFO_METHOD_HANDLE result = original_ICorJitInfo->resolveVirtualMethod(virtualMethod, implementingClass);
mc->recResolveVirtualMethod(virtualMethod, implementingClass, result);
return result;
}

// If a method's attributes have (getMethodAttribs) CORINFO_FLG_INTRINSIC set,
// getIntrinsicID() returns the intrinsic ID.
CorInfoIntrinsics interceptor_ICJI::getIntrinsicID(
Expand Down
11 changes: 11 additions & 0 deletions src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,17 @@ void interceptor_ICJI::getMethodVTableOffset (
original_ICorJitInfo->getMethodVTableOffset(method, offsetOfIndirection, offsetAfterIndirection);
}

// Find the virtual method in implementingClass that overrides virtualMethod.
// Return null if devirtualization is not possible.
CORINFO_METHOD_HANDLE interceptor_ICJI::resolveVirtualMethod(
CORINFO_METHOD_HANDLE virtualMethod,
CORINFO_CLASS_HANDLE implementingClass
)
{
mcs->AddCall("resolveVirtualMethod");
return original_ICorJitInfo->resolveVirtualMethod(virtualMethod, implementingClass);
}

// If a method's attributes have (getMethodAttribs) CORINFO_FLG_INTRINSIC set,
// getIntrinsicID() returns the intrinsic ID.
CorInfoIntrinsics interceptor_ICJI::getIntrinsicID(
Expand Down
10 changes: 10 additions & 0 deletions src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,16 @@ void interceptor_ICJI::getMethodVTableOffset (
original_ICorJitInfo->getMethodVTableOffset(method, offsetOfIndirection, offsetAfterIndirection);
}

// Find the virtual method in implementingClass that overrides virtualMethod.
// Return null if devirtualization is not possible.
CORINFO_METHOD_HANDLE interceptor_ICJI::resolveVirtualMethod(
CORINFO_METHOD_HANDLE virtualMethod,
CORINFO_CLASS_HANDLE implementingClass
)
{
return original_ICorJitInfo->resolveVirtualMethod(virtualMethod, implementingClass);
}

// If a method's attributes have (getMethodAttribs) CORINFO_FLG_INTRINSIC set,
// getIntrinsicID() returns the intrinsic ID.
CorInfoIntrinsics interceptor_ICJI::getIntrinsicID(
Expand Down
12 changes: 12 additions & 0 deletions src/ToolBox/superpmi/superpmi/icorjitinfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,18 @@ void MyICJI::getMethodVTableOffset (
jitInstance->mc->repGetMethodVTableOffset(method, offsetOfIndirection, offsetAfterIndirection);
}

// Find the virtual method in implementingClass that overrides virtualMethod.
// Return null if devirtualization is not possible.
CORINFO_METHOD_HANDLE MyICJI::resolveVirtualMethod(
CORINFO_METHOD_HANDLE virtualMethod,
CORINFO_CLASS_HANDLE implementingClass
)
{
jitInstance->mc->cr->AddCall("resolveVirtualMethod");
CORINFO_METHOD_HANDLE result = jitInstance->mc->repResolveVirtualMethod(virtualMethod, implementingClass);
return result;
}

// If a method's attributes have (getMethodAttribs) CORINFO_FLG_INTRINSIC set,
// getIntrinsicID() returns the intrinsic ID.
CorInfoIntrinsics MyICJI::getIntrinsicID(
Expand Down
21 changes: 15 additions & 6 deletions src/inc/corinfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -231,12 +231,12 @@ TODO: Talk about initializing strutures before use
#if COR_JIT_EE_VERSION > 460

// Update this one
SELECTANY const GUID JITEEVersionIdentifier = { /* 4bd06266-8ef7-4172-bec6-d3149fde7859 */
0x4bd06266,
0x8ef7,
0x4172,
{0xbe, 0xc6, 0xd3, 0x14, 0x9f, 0xde, 0x78, 0x59}
};
SELECTANY const GUID JITEEVersionIdentifier = { /* cda334f7-0020-4622-a4a5-8b8ac71ee5cf */
0xcda334f7,
0x0020,
0x4622,
{0xa4, 0xa5, 0x8b, 0x8a, 0xc7, 0x1e, 0xe5, 0xcf}
};

#else

Expand Down Expand Up @@ -2116,6 +2116,15 @@ class ICorStaticInfo
unsigned* offsetAfterIndirection /* OUT */
) = 0;

#if COR_JIT_EE_VERSION > 460
// Find the virtual method in implementingClass that overrides virtualMethod.
// Return null if devirtualization is not possible.
virtual CORINFO_METHOD_HANDLE resolveVirtualMethod(
CORINFO_METHOD_HANDLE virtualMethod, /* IN */
CORINFO_CLASS_HANDLE implementingClass /* IN */
) = 0;
#endif

// If a method's attributes have (getMethodAttribs) CORINFO_FLG_INTRINSIC set,
// getIntrinsicID() returns the intrinsic ID.
// *pMustExpand tells whether or not JIT must expand the intrinsic.
Expand Down
9 changes: 9 additions & 0 deletions src/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,8 @@ class LclVarDsc
#endif // FEATURE_SIMD
unsigned char lvRegStruct : 1; // This is a reg-sized non-field-addressed struct.

unsigned char lvClassIsExact : 1; // lvClassHandle is the exact type

union {
unsigned lvFieldLclStart; // The index of the local var representing the first field in the promoted struct
// local.
Expand Down Expand Up @@ -704,6 +706,8 @@ class LclVarDsc

typeInfo lvVerTypeInfo; // type info needed for verification

CORINFO_CLASS_HANDLE lvClassHnd; // class handle for the local, or null if not known

BYTE* lvGcLayout; // GC layout info for structs

#if ASSERTION_PROP
Expand Down Expand Up @@ -2875,6 +2879,11 @@ class Compiler
CORINFO_CALL_INFO* callInfo,
IL_OFFSET rawILOffset);

void impDevirtualizeCall(GenTreeCall* call,
GenTreePtr obj,
CORINFO_CALL_INFO* callInfo,
CORINFO_CONTEXT_HANDLE* exactContextHnd);

bool impMethodInfo_hasRetBuffArg(CORINFO_METHOD_INFO* methInfo);

GenTreePtr impFixupCallStructReturn(GenTreePtr call, CORINFO_CLASS_HANDLE retClsHnd);
Expand Down
79 changes: 75 additions & 4 deletions src/jit/flowgraph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21148,6 +21148,7 @@ void Compiler::fgInline()
}

// See if we need to replace the return value place holder.
// Also, see if this update enables further devirtualization.
fgWalkTreePre(&stmt->gtStmtExpr, fgUpdateInlineReturnExpressionPlaceHolder, (void*)this);

// See if stmt is of the form GT_COMMA(call, nop)
Expand Down Expand Up @@ -21419,11 +21420,46 @@ void Compiler::fgAttachStructInlineeToAsg(GenTreePtr tree, GenTreePtr child, COR

#endif // FEATURE_MULTIREG_RET

/*****************************************************************************
* Callback to replace the inline return expression place holder (GT_RET_EXPR)
*/
//------------------------------------------------------------------------
// fgUpdateInlineReturnExpressionPlaceHolder: callback to replace the
// inline return expression placeholder.
//
// Arguments:
// pTree -- pointer to tree to examine for updates
// data -- context data for the tree walk
//
// Returns:
// fgWalkResult indicating the walk should continue; that
// is we wish to fully explore the tree.
//
// Notes:
// Looks for GT_RET_EXPR nodes that arose from tree splitting done
// during importation for inline candidates, and replaces them.
//
// For successful inlines, substitutes the return value expression
// from the inline body for the GT_RET_EXPR.
//
// For failed inlines, rejoins the original call into the tree from
// whence it was split during importation.
//
// The code doesn't actually know if the corresponding inline
// succeeded or not; it relies on the fact that gtInlineCandidate
// initially points back at the call and is modified in place to
// the inlinee return expression if the inline is successful (see
// tail end of fgInsertInlineeBlocks for the update of iciCall).
//
// If the parent of the GT_RET_EXPR is a virtual call,
// devirtualization is attempted. This should only succeed in the
// successful inline case, when the inlinee's return value
// expression provides a better type than the return type of the
// method. Note for failed inlines, the devirtualizer can only go
// by the return type, and any devirtualization that type enabled
// would have already happened during importation.
//
// If the return type is a struct type and we're on a platform
// where structs can be returned in multiple registers, ensure the
// call has a suitable parent.

/* static */
Compiler::fgWalkResult Compiler::fgUpdateInlineReturnExpressionPlaceHolder(GenTreePtr* pTree, fgWalkData* data)
{
GenTreePtr tree = *pTree;
Expand Down Expand Up @@ -21469,6 +21505,41 @@ Compiler::fgWalkResult Compiler::fgUpdateInlineReturnExpressionPlaceHolder(GenTr
}
#endif // DEBUG
} while (tree->gtOper == GT_RET_EXPR);

// Now see if this return value expression feeds the 'this'
// object at a virtual call site.
//
// Note for void returns where the inline failed, the
// GT_RET_EXPR may be top-level.
//
// May miss cases where there are intermediaries between call
// and this, eg commas.
GenTreePtr parentTree = data->parent;

if ((parentTree != nullptr) && (parentTree->gtOper == GT_CALL))
{
GenTreeCall* call = parentTree->AsCall();
bool tryLateDevirt = call->IsVirtual() && (call->gtCallObjp == tree);

#ifdef DEBUG
tryLateDevirt = tryLateDevirt && (JitConfig.JitEnableLateDevirtualization() == 1);
#endif // DEBUG

if (tryLateDevirt)
{
#ifdef DEBUG
if (comp->verbose)
{
printf("**** Late devirt opportunity\n");
comp->gtDispTree(call);
}
#endif // DEBUG

CORINFO_CALL_INFO x = {};
x.hMethod = call->gtCallMethHnd;
comp->impDevirtualizeCall(call, tree, &x, nullptr);
}
}
}

#if FEATURE_MULTIREG_RET
Expand Down
Loading