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

JIT: after devirtualization try undoing box and invoking unboxed entry #14698

Merged
merged 2 commits into from
Oct 30, 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
5 changes: 5 additions & 0 deletions src/ToolBox/superpmi/superpmi-shared/icorjitinfoimpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,11 @@ CORINFO_METHOD_HANDLE resolveVirtualMethod(CORINFO_METHOD_HANDLE virtualMethod,
CORINFO_CLASS_HANDLE implementingClass,
CORINFO_CONTEXT_HANDLE ownerType);

// Get the unboxed entry point for a method, if possible.
CORINFO_METHOD_HANDLE getUnboxedEntry(
CORINFO_METHOD_HANDLE ftn,
bool* requiresInstMethodTableArg /* OUT */);

// Given T, return the type of the default EqualityComparer<T>.
// Returns null if the type can't be determined exactly.
CORINFO_CLASS_HANDLE getDefaultEqualityComparerClass(CORINFO_CLASS_HANDLE elemType);
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 @@ -124,6 +124,7 @@ LWM(GetThreadTLSIndex, DWORD, DLD)
LWM(GetTokenTypeAsHandle, GetTokenTypeAsHandleValue, DWORDLONG)
LWM(GetTypeForBox, DWORDLONG, DWORDLONG)
LWM(GetTypeForPrimitiveValueClass, DWORDLONG, DWORD)
LWM(GetUnboxedEntry, DWORDLONG, DLD);
LWM(GetUnBoxHelper, DWORDLONG, DWORD)
LWM(GetUnmanagedCallConv, DWORDLONG, DWORD)
LWM(GetVarArgsHandle, GetVarArgsHandleValue, DLDL)
Expand Down
49 changes: 49 additions & 0 deletions src/ToolBox/superpmi/superpmi-shared/methodcontext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3018,6 +3018,55 @@ CORINFO_METHOD_HANDLE MethodContext::repResolveVirtualMethod(CORINFO_METHOD_HAND
return (CORINFO_METHOD_HANDLE)result;
}

void MethodContext::recGetUnboxedEntry(CORINFO_METHOD_HANDLE ftn,
bool *requiresInstMethodTableArg,
CORINFO_METHOD_HANDLE result)
{
if (GetUnboxedEntry == nullptr)
{
GetUnboxedEntry = new LightWeightMap<DWORDLONG, DLD>();
}

DWORDLONG key = (DWORDLONG) ftn;
DLD value;
value.A = (DWORDLONG) result;
if (requiresInstMethodTableArg != nullptr)
{
value.B = (DWORD) *requiresInstMethodTableArg ? 1 : 0;
}
else
{
value.B = 0;
}
GetUnboxedEntry->Add(key, value);
DEBUG_REC(dmpGetUnboxedEntry(key, value));
}

void MethodContext::dmpGetUnboxedEntry(DWORDLONG key, DLD value)
{
printf("GetUnboxedEntry ftn-%016llX, result-%016llX, requires-inst-%u",
key, value.A, value.B);
}

CORINFO_METHOD_HANDLE MethodContext::repGetUnboxedEntry(CORINFO_METHOD_HANDLE ftn,
bool* requiresInstMethodTableArg)
{
DWORDLONG key = (DWORDLONG)ftn;

AssertCodeMsg(GetUnboxedEntry != nullptr, EXCEPTIONCODE_MC, "No GetUnboxedEntry map for %016llX", key);
AssertCodeMsg(GetUnboxedEntry->GetIndex(key) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", key);
DLD result = GetUnboxedEntry->Get(key);

DEBUG_REP(dmpGetUnboxedEntry(key, result));

if (requiresInstMethodTableArg != nullptr)
{
*requiresInstMethodTableArg = (result.B == 1);
}

return (CORINFO_METHOD_HANDLE)(result.A);
}

void MethodContext::recGetDefaultEqualityComparerClass(CORINFO_CLASS_HANDLE cls, CORINFO_CLASS_HANDLE result)
{
if (GetDefaultEqualityComparerClass == nullptr)
Expand Down
10 changes: 9 additions & 1 deletion src/ToolBox/superpmi/superpmi-shared/methodcontext.h
Original file line number Diff line number Diff line change
Expand Up @@ -877,6 +877,13 @@ class MethodContext
CORINFO_CLASS_HANDLE implClass,
CORINFO_CONTEXT_HANDLE ownerType);

void recGetUnboxedEntry(CORINFO_METHOD_HANDLE ftn,
bool* requiresInstMethodTableArg,
CORINFO_METHOD_HANDLE result);
void dmpGetUnboxedEntry(DWORDLONG key, DLD value);
CORINFO_METHOD_HANDLE repGetUnboxedEntry(CORINFO_METHOD_HANDLE ftn,
bool* requiresInstMethodTableArg);

void recGetDefaultEqualityComparerClass(CORINFO_CLASS_HANDLE cls, CORINFO_CLASS_HANDLE result);
void dmpGetDefaultEqualityComparerClass(DWORDLONG key, DWORDLONG value);
CORINFO_CLASS_HANDLE repGetDefaultEqualityComparerClass(CORINFO_CLASS_HANDLE cls);
Expand Down Expand Up @@ -1269,7 +1276,7 @@ class MethodContext
};

// ********************* Please keep this up-to-date to ease adding more ***************
// Highest packet number: 164
// Highest packet number: 165
// *************************************************************************************
enum mcPackets
{
Expand Down Expand Up @@ -1384,6 +1391,7 @@ enum mcPackets
Packet_GetTokenTypeAsHandle = 89,
Packet_GetTypeForBox = 90,
Packet_GetTypeForPrimitiveValueClass = 91,
Packet_GetUnboxedEntry = 165, // Added 10/26/17
Packet_GetUnBoxHelper = 92,
Packet_GetReadyToRunHelper = 150, // Added 10/10/2014
Packet_GetReadyToRunDelegateCtorHelper = 157, // Added 3/30/2016
Expand Down
15 changes: 15 additions & 0 deletions src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,21 @@ CORINFO_METHOD_HANDLE interceptor_ICJI::resolveVirtualMethod(CORINFO_METHOD_HAND
return result;
}

// Get the unboxed entry point for a method, if possible.
CORINFO_METHOD_HANDLE interceptor_ICJI::getUnboxedEntry(CORINFO_METHOD_HANDLE ftn, bool* requiresInstMethodTableArg)
{
mc->cr->AddCall("getUnboxedEntry");
bool localRequiresInstMethodTableArg = false;
CORINFO_METHOD_HANDLE result =
original_ICorJitInfo->getUnboxedEntry(ftn, &localRequiresInstMethodTableArg);
mc->recGetUnboxedEntry(ftn, &localRequiresInstMethodTableArg, result);
if (requiresInstMethodTableArg != nullptr)
{
*requiresInstMethodTableArg = localRequiresInstMethodTableArg;
}
return result;
}

// Given T, return the type of the default EqualityComparer<T>.
// Returns null if the type can't be determined exactly.
CORINFO_CLASS_HANDLE interceptor_ICJI::getDefaultEqualityComparerClass(CORINFO_CLASS_HANDLE cls)
Expand Down
7 changes: 7 additions & 0 deletions src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,13 @@ CORINFO_METHOD_HANDLE interceptor_ICJI::resolveVirtualMethod(CORINFO_METHOD_HAND
return original_ICorJitInfo->resolveVirtualMethod(virtualMethod, implementingClass, ownerType);
}

// Get the unboxed entry point for a method, if possible.
CORINFO_METHOD_HANDLE interceptor_ICJI::getUnboxedEntry(CORINFO_METHOD_HANDLE ftn, bool* requiresInstMethodTableArg)
{
mcs->AddCall("getUnboxedEntry");
return original_ICorJitInfo->getUnboxedEntry(ftn, requiresInstMethodTableArg);
}

// Given T, return the type of the default EqualityComparer<T>.
// Returns null if the type can't be determined exactly.
CORINFO_CLASS_HANDLE interceptor_ICJI::getDefaultEqualityComparerClass(CORINFO_CLASS_HANDLE cls)
Expand Down
6 changes: 6 additions & 0 deletions src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,12 @@ void interceptor_ICJI::getMethodVTableOffset(CORINFO_METHOD_HANDLE method,
original_ICorJitInfo->getMethodVTableOffset(method, offsetOfIndirection, offsetAfterIndirection, isRelative);
}

// Get the unboxed entry point for a method, if possible.
CORINFO_METHOD_HANDLE interceptor_ICJI::getUnboxedEntry(CORINFO_METHOD_HANDLE ftn, bool* requiresInstMethodTableArg)
{
return original_ICorJitInfo->getUnboxedEntry(ftn, requiresInstMethodTableArg);
}

// 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,
Expand Down
9 changes: 9 additions & 0 deletions src/ToolBox/superpmi/superpmi/icorjitinfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,15 @@ CORINFO_METHOD_HANDLE MyICJI::resolveVirtualMethod(CORINFO_METHOD_HANDLE virtua
return result;
}

// Get the unboxed entry point for a method, if possible.
CORINFO_METHOD_HANDLE MyICJI::getUnboxedEntry(CORINFO_METHOD_HANDLE ftn, bool* requiresInstMethodTableArg)
{
jitInstance->mc->cr->AddCall("getUnboxedEntry");
CORINFO_METHOD_HANDLE result =
jitInstance->mc->repGetUnboxedEntry(ftn, requiresInstMethodTableArg);
return result;
}

// Given T, return the type of the default EqualityComparer<T>.
// Returns null if the type can't be determined exactly.
CORINFO_CLASS_HANDLE MyICJI::getDefaultEqualityComparerClass(CORINFO_CLASS_HANDLE cls)
Expand Down
16 changes: 11 additions & 5 deletions src/inc/corinfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -213,11 +213,11 @@ TODO: Talk about initializing strutures before use
#define SELECTANY extern __declspec(selectany)
#endif

SELECTANY const GUID JITEEVersionIdentifier = { /* 860df660-2919-440a-ad6d-865f014e5360 */
0x860df660,
0x2919,
0x440a,
{ 0xad, 0x6d, 0x86, 0x5f, 0x01, 0x4e, 0x53, 0x60 }
SELECTANY const GUID JITEEVersionIdentifier = { /* b6af83a1-ca48-46c6-b7b0-5d7d6a79a5c5 */
0xb6af83a1,
0xca48,
0x46c6,
{0xb7, 0xb0, 0x5d, 0x7d, 0x6a, 0x79, 0xa5, 0xc5}
};

//////////////////////////////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -2094,6 +2094,12 @@ class ICorStaticInfo
CORINFO_CONTEXT_HANDLE ownerType = NULL /* IN */
) = 0;

// Get the unboxed entry point for a method, if possible.
virtual CORINFO_METHOD_HANDLE getUnboxedEntry(
CORINFO_METHOD_HANDLE ftn,
bool* requiresInstMethodTableArg = NULL /* OUT */
) = 0;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh Good, a Jit Interface change

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, need a new guid, don't I....


// Given T, return the type of the default EqualityComparer<T>.
// Returns null if the type can't be determined exactly.
virtual CORINFO_CLASS_HANDLE getDefaultEqualityComparerClass(
Expand Down
3 changes: 2 additions & 1 deletion src/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -2283,7 +2283,8 @@ class Compiler
BR_REMOVE_AND_NARROW, // remove effects, minimize remaining work, return possibly narrowed source tree
BR_REMOVE_AND_NARROW_WANT_TYPE_HANDLE, // remove effects and minimize remaining work, return type handle tree
BR_REMOVE_BUT_NOT_NARROW, // remove effects, return original source tree
BR_DONT_REMOVE // just check if removal is possible
BR_DONT_REMOVE, // just check if removal is possible
BR_MAKE_LOCAL_COPY // revise box to copy to temp local and return local's address
};

GenTree* gtTryRemoveBoxUpstreamEffects(GenTree* tree, BoxRemovalOptions options = BR_REMOVE_AND_NARROW);
Expand Down
93 changes: 77 additions & 16 deletions src/jit/gentree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13150,27 +13150,21 @@ GenTreePtr Compiler::gtFoldExprSpecial(GenTreePtr tree)

GenTree* Compiler::gtTryRemoveBoxUpstreamEffects(GenTree* op, BoxRemovalOptions options)
{
JITDUMP("gtTryRemoveBoxUpstreamEffects called for [%06u]\n", dspTreeID(op));
assert(op->IsBoxedValue());

// grab related parts for the optimization
GenTree* asgStmt = op->gtBox.gtAsgStmtWhenInlinedBoxValue;
GenTreeBox* box = op->AsBox();
GenTree* asgStmt = box->gtAsgStmtWhenInlinedBoxValue;
GenTree* copyStmt = box->gtCopyStmtWhenInlinedBoxValue;

assert(asgStmt->gtOper == GT_STMT);
GenTree* copyStmt = op->gtBox.gtCopyStmtWhenInlinedBoxValue;
assert(copyStmt->gtOper == GT_STMT);

#ifdef DEBUG
if (verbose)
{
printf("\n%s to remove side effects of BOX (valuetype)\n",
options == BR_DONT_REMOVE ? "Checking if it is possible" : "Attempting");
gtDispTree(op);
printf("\nWith assign\n");
gtDispTree(asgStmt);
printf("\nAnd copy\n");
gtDispTree(copyStmt);
}
#endif
JITDUMP("gtTryRemoveBoxUpstreamEffects: %s to %s of BOX (valuetype)"
" [%06u] (assign/newobj [%06u] copy [%06u])\n",
(options == BR_DONT_REMOVE) ? "checking if it is possible" : "attempting",
(options == BR_MAKE_LOCAL_COPY) ? "make local unboxed version" : "remove side effects", dspTreeID(op),
dspTreeID(asgStmt), dspTreeID(copyStmt));

// If we don't recognize the form of the assign, bail.
GenTree* asg = asgStmt->gtStmt.gtStmtExpr;
Expand Down Expand Up @@ -13205,7 +13199,7 @@ GenTree* Compiler::gtTryRemoveBoxUpstreamEffects(GenTree* op, BoxRemovalOptions
if (newobjArgs == nullptr)
{
assert(newobjCall->IsHelperCall(this, CORINFO_HELP_READYTORUN_NEW));
JITDUMP("bailing; newobj via R2R helper\n");
JITDUMP(" bailing; newobj via R2R helper\n");
return nullptr;
}

Expand Down Expand Up @@ -13241,6 +13235,73 @@ GenTree* Compiler::gtTryRemoveBoxUpstreamEffects(GenTree* op, BoxRemovalOptions
return nullptr;
}

// Handle case where we are optimizing the box into a local copy
if (options == BR_MAKE_LOCAL_COPY)
{
// Drill into the box to get at the box temp local and the box type
GenTree* boxTemp = box->BoxOp();
assert(boxTemp->IsLocal());
const unsigned boxTempLcl = boxTemp->AsLclVar()->GetLclNum();
assert(lvaTable[boxTempLcl].lvType == TYP_REF);
CORINFO_CLASS_HANDLE boxClass = lvaTable[boxTempLcl].lvClassHnd;
assert(boxClass != nullptr);

// Verify that the copyDst has the expected shape
// (blk|obj|ind (add (boxTempLcl, ptr-size)))
//
// The shape here is constrained to the patterns we produce
// over in impImportAndPushBox for the inlined box case.
GenTree* copyDst = copy->gtOp.gtOp1;

if (!copyDst->OperIs(GT_BLK, GT_IND, GT_OBJ))
{
JITDUMP("Unexpected copy dest operator %s\n", GenTree::OpName(copyDst->gtOper));
return nullptr;
}

GenTree* copyDstAddr = copyDst->gtOp.gtOp1;
if (copyDstAddr->OperGet() != GT_ADD)
{
JITDUMP("Unexpected copy dest address tree\n");
return nullptr;
}

GenTree* copyDstAddrOp1 = copyDstAddr->gtOp.gtOp1;
if ((copyDstAddrOp1->OperGet() != GT_LCL_VAR) || (copyDstAddrOp1->gtLclVarCommon.gtLclNum != boxTempLcl))
{
JITDUMP("Unexpected copy dest address 1st addend\n");
return nullptr;
}

GenTree* copyDstAddrOp2 = copyDstAddr->gtOp.gtOp2;
if (!copyDstAddrOp2->IsIntegralConst(TARGET_POINTER_SIZE))
{
JITDUMP("Unexpected copy dest address 2nd addend\n");
return nullptr;
}

// Screening checks have all passed. Do the transformation.
//
// Retype the box temp to be a struct
JITDUMP("Retyping box temp V%02u to struct %s\n", boxTempLcl, eeGetClassName(boxClass));
lvaTable[boxTempLcl].lvType = TYP_UNDEF;
const bool isUnsafeValueClass = false;
lvaSetStruct(boxTempLcl, boxClass, isUnsafeValueClass);

// Remove the newobj and assigment to box temp
JITDUMP("Bashing NEWOBJ [%06u] to NOP\n", dspTreeID(asg));
asg->gtBashToNOP();

// Update the copy from the value to be boxed to the box temp
GenTree* newDst = gtNewOperNode(GT_ADDR, TYP_BYREF, gtNewLclvNode(boxTempLcl, TYP_STRUCT));
copyDst->gtOp.gtOp1 = newDst;

// Return the address of the now-struct typed box temp
GenTree* retValue = gtNewOperNode(GT_ADDR, TYP_BYREF, gtNewLclvNode(boxTempLcl, TYP_STRUCT));

return retValue;
}

// If the copy is a struct copy, make sure we know how to isolate
// any source side effects.
GenTree* copySrc = copy->gtOp.gtOp2;
Expand Down
Loading