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

Commit 215ee89

Browse files
committed
JIT: optimize calls on boxed objects
For calls with boxed this objects, invoke the unboxed entry point if available and modify the box to copy the original value or struct to a local. Pass the address of this local as the first argument to the unboxed entry point. Defer for cases where the unboxed entry point requires an extra type parameter. This may enable subsequent inlining of the method, and if the method has other boxed parameters, may enable undoing those boxes as well. The jit is not yet as proficient at eliminating the struct copies, but they are present with or without this change, and some may even be required when the methods mutate the fields of `this`. Closes #14213.
1 parent 274c961 commit 215ee89

File tree

16 files changed

+344
-61
lines changed

16 files changed

+344
-61
lines changed

src/ToolBox/superpmi/superpmi-shared/icorjitinfoimpl.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,11 @@ CORINFO_METHOD_HANDLE resolveVirtualMethod(CORINFO_METHOD_HANDLE virtualMethod,
121121
CORINFO_CLASS_HANDLE implementingClass,
122122
CORINFO_CONTEXT_HANDLE ownerType);
123123

124+
// Get the unboxed entry point for a method, if possible.
125+
CORINFO_METHOD_HANDLE getUnboxedEntry(
126+
CORINFO_METHOD_HANDLE ftn,
127+
bool* requiresInstMethodTableArg /* OUT */);
128+
124129
// Given T, return the type of the default EqualityComparer<T>.
125130
// Returns null if the type can't be determined exactly.
126131
CORINFO_CLASS_HANDLE getDefaultEqualityComparerClass(CORINFO_CLASS_HANDLE elemType);

src/ToolBox/superpmi/superpmi-shared/lwmlist.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ LWM(GetThreadTLSIndex, DWORD, DLD)
124124
LWM(GetTokenTypeAsHandle, GetTokenTypeAsHandleValue, DWORDLONG)
125125
LWM(GetTypeForBox, DWORDLONG, DWORDLONG)
126126
LWM(GetTypeForPrimitiveValueClass, DWORDLONG, DWORD)
127+
LWM(GetUnboxedEntry, DWORDLONG, DLD);
127128
LWM(GetUnBoxHelper, DWORDLONG, DWORD)
128129
LWM(GetUnmanagedCallConv, DWORDLONG, DWORD)
129130
LWM(GetVarArgsHandle, GetVarArgsHandleValue, DLDL)

src/ToolBox/superpmi/superpmi-shared/methodcontext.cpp

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3018,6 +3018,55 @@ CORINFO_METHOD_HANDLE MethodContext::repResolveVirtualMethod(CORINFO_METHOD_HAND
30183018
return (CORINFO_METHOD_HANDLE)result;
30193019
}
30203020

3021+
void MethodContext::recGetUnboxedEntry(CORINFO_METHOD_HANDLE ftn,
3022+
bool *requiresInstMethodTableArg,
3023+
CORINFO_METHOD_HANDLE result)
3024+
{
3025+
if (GetUnboxedEntry == nullptr)
3026+
{
3027+
GetUnboxedEntry = new LightWeightMap<DWORDLONG, DLD>();
3028+
}
3029+
3030+
DWORDLONG key = (DWORDLONG) ftn;
3031+
DLD value;
3032+
value.A = (DWORDLONG) result;
3033+
if (requiresInstMethodTableArg != nullptr)
3034+
{
3035+
value.B = (DWORD) *requiresInstMethodTableArg ? 1 : 0;
3036+
}
3037+
else
3038+
{
3039+
value.B = 0;
3040+
}
3041+
GetUnboxedEntry->Add(key, value);
3042+
DEBUG_REC(dmpGetUnboxedEntry(key, value));
3043+
}
3044+
3045+
void MethodContext::dmpGetUnboxedEntry(DWORDLONG key, DLD value)
3046+
{
3047+
printf("GetUnboxedEntry ftn-%016llX, result-%016llX, requires-inst-%u",
3048+
key, value.A, value.B);
3049+
}
3050+
3051+
CORINFO_METHOD_HANDLE MethodContext::repGetUnboxedEntry(CORINFO_METHOD_HANDLE ftn,
3052+
bool* requiresInstMethodTableArg)
3053+
{
3054+
DWORDLONG key = (DWORDLONG)ftn;
3055+
3056+
AssertCodeMsg(GetUnboxedEntry != nullptr, EXCEPTIONCODE_MC, "No GetUnboxedEntry map for %016llX", key);
3057+
AssertCodeMsg(GetUnboxedEntry->GetIndex(key) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", key);
3058+
DLD result = GetUnboxedEntry->Get(key);
3059+
3060+
DEBUG_REP(dmpGetUnboxedEntry(key, result));
3061+
3062+
if (requiresInstMethodTableArg != nullptr)
3063+
{
3064+
*requiresInstMethodTableArg = (result.B == 1);
3065+
}
3066+
3067+
return (CORINFO_METHOD_HANDLE)(result.A);
3068+
}
3069+
30213070
void MethodContext::recGetDefaultEqualityComparerClass(CORINFO_CLASS_HANDLE cls, CORINFO_CLASS_HANDLE result)
30223071
{
30233072
if (GetDefaultEqualityComparerClass == nullptr)

src/ToolBox/superpmi/superpmi-shared/methodcontext.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -877,6 +877,13 @@ class MethodContext
877877
CORINFO_CLASS_HANDLE implClass,
878878
CORINFO_CONTEXT_HANDLE ownerType);
879879

880+
void recGetUnboxedEntry(CORINFO_METHOD_HANDLE ftn,
881+
bool* requiresInstMethodTableArg,
882+
CORINFO_METHOD_HANDLE result);
883+
void dmpGetUnboxedEntry(DWORDLONG key, DLD value);
884+
CORINFO_METHOD_HANDLE repGetUnboxedEntry(CORINFO_METHOD_HANDLE ftn,
885+
bool* requiresInstMethodTableArg);
886+
880887
void recGetDefaultEqualityComparerClass(CORINFO_CLASS_HANDLE cls, CORINFO_CLASS_HANDLE result);
881888
void dmpGetDefaultEqualityComparerClass(DWORDLONG key, DWORDLONG value);
882889
CORINFO_CLASS_HANDLE repGetDefaultEqualityComparerClass(CORINFO_CLASS_HANDLE cls);
@@ -1269,7 +1276,7 @@ class MethodContext
12691276
};
12701277

12711278
// ********************* Please keep this up-to-date to ease adding more ***************
1272-
// Highest packet number: 164
1279+
// Highest packet number: 165
12731280
// *************************************************************************************
12741281
enum mcPackets
12751282
{
@@ -1384,6 +1391,7 @@ enum mcPackets
13841391
Packet_GetTokenTypeAsHandle = 89,
13851392
Packet_GetTypeForBox = 90,
13861393
Packet_GetTypeForPrimitiveValueClass = 91,
1394+
Packet_GetUnboxedEntry = 165, // Added 10/26/17
13871395
Packet_GetUnBoxHelper = 92,
13881396
Packet_GetReadyToRunHelper = 150, // Added 10/10/2014
13891397
Packet_GetReadyToRunDelegateCtorHelper = 157, // Added 3/30/2016

src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,21 @@ CORINFO_METHOD_HANDLE interceptor_ICJI::resolveVirtualMethod(CORINFO_METHOD_HAND
242242
return result;
243243
}
244244

245+
// Get the unboxed entry point for a method, if possible.
246+
CORINFO_METHOD_HANDLE interceptor_ICJI::getUnboxedEntry(CORINFO_METHOD_HANDLE ftn, bool* requiresInstMethodTableArg)
247+
{
248+
mc->cr->AddCall("getUnboxedEntry");
249+
bool localRequiresInstMethodTableArg = false;
250+
CORINFO_METHOD_HANDLE result =
251+
original_ICorJitInfo->getUnboxedEntry(ftn, &localRequiresInstMethodTableArg);
252+
mc->recGetUnboxedEntry(ftn, &localRequiresInstMethodTableArg, result);
253+
if (requiresInstMethodTableArg != nullptr)
254+
{
255+
*requiresInstMethodTableArg = localRequiresInstMethodTableArg;
256+
}
257+
return result;
258+
}
259+
245260
// Given T, return the type of the default EqualityComparer<T>.
246261
// Returns null if the type can't be determined exactly.
247262
CORINFO_CLASS_HANDLE interceptor_ICJI::getDefaultEqualityComparerClass(CORINFO_CLASS_HANDLE cls)

src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,13 @@ CORINFO_METHOD_HANDLE interceptor_ICJI::resolveVirtualMethod(CORINFO_METHOD_HAND
163163
return original_ICorJitInfo->resolveVirtualMethod(virtualMethod, implementingClass, ownerType);
164164
}
165165

166+
// Get the unboxed entry point for a method, if possible.
167+
CORINFO_METHOD_HANDLE interceptor_ICJI::getUnboxedEntry(CORINFO_METHOD_HANDLE ftn, bool* requiresInstMethodTableArg)
168+
{
169+
mcs->AddCall("getUnboxedEntry");
170+
return original_ICorJitInfo->getUnboxedEntry(ftn, requiresInstMethodTableArg);
171+
}
172+
166173
// Given T, return the type of the default EqualityComparer<T>.
167174
// Returns null if the type can't be determined exactly.
168175
CORINFO_CLASS_HANDLE interceptor_ICJI::getDefaultEqualityComparerClass(CORINFO_CLASS_HANDLE cls)

src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,12 @@ void interceptor_ICJI::getMethodVTableOffset(CORINFO_METHOD_HANDLE method,
141141
original_ICorJitInfo->getMethodVTableOffset(method, offsetOfIndirection, offsetAfterIndirection, isRelative);
142142
}
143143

144+
// Get the unboxed entry point for a method, if possible.
145+
CORINFO_METHOD_HANDLE interceptor_ICJI::getUnboxedEntry(CORINFO_METHOD_HANDLE ftn, bool* requiresInstMethodTableArg)
146+
{
147+
return original_ICorJitInfo->getUnboxedEntry(ftn, requiresInstMethodTableArg);
148+
}
149+
144150
// Find the virtual method in implementingClass that overrides virtualMethod.
145151
// Return null if devirtualization is not possible.
146152
CORINFO_METHOD_HANDLE interceptor_ICJI::resolveVirtualMethod(CORINFO_METHOD_HANDLE virtualMethod,

src/ToolBox/superpmi/superpmi/icorjitinfo.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,15 @@ CORINFO_METHOD_HANDLE MyICJI::resolveVirtualMethod(CORINFO_METHOD_HANDLE virtua
185185
return result;
186186
}
187187

188+
// Get the unboxed entry point for a method, if possible.
189+
CORINFO_METHOD_HANDLE MyICJI::getUnboxedEntry(CORINFO_METHOD_HANDLE ftn, bool* requiresInstMethodTableArg)
190+
{
191+
jitInstance->mc->cr->AddCall("getUnboxedEntry");
192+
CORINFO_METHOD_HANDLE result =
193+
jitInstance->mc->repGetUnboxedEntry(ftn, requiresInstMethodTableArg);
194+
return result;
195+
}
196+
188197
// Given T, return the type of the default EqualityComparer<T>.
189198
// Returns null if the type can't be determined exactly.
190199
CORINFO_CLASS_HANDLE MyICJI::getDefaultEqualityComparerClass(CORINFO_CLASS_HANDLE cls)

src/inc/corinfo.h

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -213,11 +213,11 @@ TODO: Talk about initializing strutures before use
213213
#define SELECTANY extern __declspec(selectany)
214214
#endif
215215

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

223223
//////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -2094,6 +2094,12 @@ class ICorStaticInfo
20942094
CORINFO_CONTEXT_HANDLE ownerType = NULL /* IN */
20952095
) = 0;
20962096

2097+
// Get the unboxed entry point for a method, if possible.
2098+
virtual CORINFO_METHOD_HANDLE getUnboxedEntry(
2099+
CORINFO_METHOD_HANDLE ftn,
2100+
bool* requiresInstMethodTableArg = NULL /* OUT */
2101+
) = 0;
2102+
20972103
// Given T, return the type of the default EqualityComparer<T>.
20982104
// Returns null if the type can't be determined exactly.
20992105
virtual CORINFO_CLASS_HANDLE getDefaultEqualityComparerClass(

src/jit/compiler.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2283,7 +2283,8 @@ class Compiler
22832283
BR_REMOVE_AND_NARROW, // remove effects, minimize remaining work, return possibly narrowed source tree
22842284
BR_REMOVE_AND_NARROW_WANT_TYPE_HANDLE, // remove effects and minimize remaining work, return type handle tree
22852285
BR_REMOVE_BUT_NOT_NARROW, // remove effects, return original source tree
2286-
BR_DONT_REMOVE // just check if removal is possible
2286+
BR_DONT_REMOVE, // just check if removal is possible
2287+
BR_MAKE_LOCAL_COPY // revise box to copy to temp local and return local's address
22872288
};
22882289

22892290
GenTree* gtTryRemoveBoxUpstreamEffects(GenTree* tree, BoxRemovalOptions options = BR_REMOVE_AND_NARROW);

src/jit/gentree.cpp

Lines changed: 77 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13156,27 +13156,21 @@ GenTreePtr Compiler::gtFoldExprSpecial(GenTreePtr tree)
1315613156

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

1316213161
// grab related parts for the optimization
13163-
GenTree* asgStmt = op->gtBox.gtAsgStmtWhenInlinedBoxValue;
13162+
GenTreeBox* box = op->AsBox();
13163+
GenTree* asgStmt = box->gtAsgStmtWhenInlinedBoxValue;
13164+
GenTree* copyStmt = box->gtCopyStmtWhenInlinedBoxValue;
13165+
1316413166
assert(asgStmt->gtOper == GT_STMT);
13165-
GenTree* copyStmt = op->gtBox.gtCopyStmtWhenInlinedBoxValue;
1316613167
assert(copyStmt->gtOper == GT_STMT);
1316713168

13168-
#ifdef DEBUG
13169-
if (verbose)
13170-
{
13171-
printf("\n%s to remove side effects of BOX (valuetype)\n",
13172-
options == BR_DONT_REMOVE ? "Checking if it is possible" : "Attempting");
13173-
gtDispTree(op);
13174-
printf("\nWith assign\n");
13175-
gtDispTree(asgStmt);
13176-
printf("\nAnd copy\n");
13177-
gtDispTree(copyStmt);
13178-
}
13179-
#endif
13169+
JITDUMP("gtTryRemoveBoxUpstreamEffects: %s to %s of BOX (valuetype)"
13170+
" [%06u] (assign/newobj [%06u] copy [%06u])\n",
13171+
(options == BR_DONT_REMOVE) ? "checking if it is possible" : "attempting",
13172+
(options == BR_MAKE_LOCAL_COPY) ? "make local unboxed version" : "remove side effects", dspTreeID(op),
13173+
dspTreeID(asgStmt), dspTreeID(copyStmt));
1318013174

1318113175
// If we don't recognize the form of the assign, bail.
1318213176
GenTree* asg = asgStmt->gtStmt.gtStmtExpr;
@@ -13211,7 +13205,7 @@ GenTree* Compiler::gtTryRemoveBoxUpstreamEffects(GenTree* op, BoxRemovalOptions
1321113205
if (newobjArgs == nullptr)
1321213206
{
1321313207
assert(newobjCall->IsHelperCall(this, CORINFO_HELP_READYTORUN_NEW));
13214-
JITDUMP("bailing; newobj via R2R helper\n");
13208+
JITDUMP(" bailing; newobj via R2R helper\n");
1321513209
return nullptr;
1321613210
}
1321713211

@@ -13247,6 +13241,73 @@ GenTree* Compiler::gtTryRemoveBoxUpstreamEffects(GenTree* op, BoxRemovalOptions
1324713241
return nullptr;
1324813242
}
1324913243

13244+
// Handle case where we are optimizing the box into a local copy
13245+
if (options == BR_MAKE_LOCAL_COPY)
13246+
{
13247+
// Drill into the box to get at the box temp local and the box type
13248+
GenTree* boxTemp = box->BoxOp();
13249+
assert(boxTemp->IsLocal());
13250+
const unsigned boxTempLcl = boxTemp->AsLclVar()->GetLclNum();
13251+
assert(lvaTable[boxTempLcl].lvType == TYP_REF);
13252+
CORINFO_CLASS_HANDLE boxClass = lvaTable[boxTempLcl].lvClassHnd;
13253+
assert(boxClass != nullptr);
13254+
13255+
// Verify that the copyDst has the expected shape
13256+
// (blk|obj|ind (add (boxTempLcl, ptr-size)))
13257+
//
13258+
// The shape here is constrained to the patterns we produce
13259+
// over in impImportAndPushBox for the inlined box case.
13260+
GenTree* copyDst = copy->gtOp.gtOp1;
13261+
13262+
if (!copyDst->OperIs(GT_BLK, GT_IND, GT_OBJ))
13263+
{
13264+
JITDUMP("Unexpected copy dest operator %s\n", GenTree::OpName(copyDst->gtOper));
13265+
return nullptr;
13266+
}
13267+
13268+
GenTree* copyDstAddr = copyDst->gtOp.gtOp1;
13269+
if (copyDstAddr->OperGet() != GT_ADD)
13270+
{
13271+
JITDUMP("Unexpected copy dest address tree\n");
13272+
return nullptr;
13273+
}
13274+
13275+
GenTree* copyDstAddrOp1 = copyDstAddr->gtOp.gtOp1;
13276+
if ((copyDstAddrOp1->OperGet() != GT_LCL_VAR) || (copyDstAddrOp1->gtLclVarCommon.gtLclNum != boxTempLcl))
13277+
{
13278+
JITDUMP("Unexpected copy dest address 1st addend\n");
13279+
return nullptr;
13280+
}
13281+
13282+
GenTree* copyDstAddrOp2 = copyDstAddr->gtOp.gtOp2;
13283+
if (!copyDstAddrOp2->IsIntegralConst(TARGET_POINTER_SIZE))
13284+
{
13285+
JITDUMP("Unexpected copy dest address 2nd addend\n");
13286+
return nullptr;
13287+
}
13288+
13289+
// Screening checks have all passed. Do the transformation.
13290+
//
13291+
// Retype the box temp to be a struct
13292+
JITDUMP("Retyping box temp V%02u to struct %s\n", boxTempLcl, eeGetClassName(boxClass));
13293+
lvaTable[boxTempLcl].lvType = TYP_UNDEF;
13294+
const bool isUnsafeValueClass = false;
13295+
lvaSetStruct(boxTempLcl, boxClass, isUnsafeValueClass);
13296+
13297+
// Remove the newobj and assigment to box temp
13298+
JITDUMP("Bashing NEWOBJ [%06u] to NOP\n", dspTreeID(asg));
13299+
asg->gtBashToNOP();
13300+
13301+
// Update the copy from the value to be boxed to the box temp
13302+
GenTree* newDst = gtNewOperNode(GT_ADDR, TYP_BYREF, gtNewLclvNode(boxTempLcl, TYP_STRUCT));
13303+
copyDst->gtOp.gtOp1 = newDst;
13304+
13305+
// Return the address of the now-struct typed box temp
13306+
GenTree* retValue = gtNewOperNode(GT_ADDR, TYP_BYREF, gtNewLclvNode(boxTempLcl, TYP_STRUCT));
13307+
13308+
return retValue;
13309+
}
13310+
1325013311
// If the copy is a struct copy, make sure we know how to isolate
1325113312
// any source side effects.
1325213313
GenTree* copySrc = copy->gtOp.gtOp2;

0 commit comments

Comments
 (0)