@@ -21009,105 +21009,118 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call,
2100921009
2101021010 if (unboxedEntryMethod != nullptr)
2101121011 {
21012- bool optimizedTheBox = false;
21012+ bool didOptimize = false;
21013+ bool canOptimize = false;
2101321014
2101421015 // If the 'this' object is a local box, see if we can revise things
2101521016 // to not require boxing.
2101621017 //
21017- if (thisObj->IsBoxedValue() && !isExplicitTailCall )
21018+ if (thisObj->IsBoxedValue())
2101821019 {
21019- // Since the call is the only consumer of the box, we know the box can't escape
21020- // since it is being passed an interior pointer.
21021- //
21022- // So, revise the box to simply create a local copy, use the address of that copy
21023- // as the this pointer, and update the entry point to the unboxed entry.
21024- //
21025- // Ideally, we then inline the boxed method and and if it turns out not to modify
21026- // the copy, we can undo the copy too.
21027- if (requiresInstMethodTableArg)
21020+ if (isExplicitTailCall)
2102821021 {
21029- // Perform a trial box removal and ask for the type handle tree that fed the box.
21022+ // We won't optimize away boxes that feed explicit tail calls, as ensuring
21023+ // we get the right tail call info is tricky (we'd need to pass an updated
21024+ // sig and resolved token back to some callers).
2103021025 //
21031- JITDUMP("Unboxed entry needs method table arg...\n");
21032- GenTree* methodTableArg = gtTryRemoveBoxUpstreamEffects(thisObj, BR_DONT_REMOVE_WANT_TYPE_HANDLE);
21033-
21034- if (methodTableArg != nullptr)
21026+ canOptimize = false;
21027+ }
21028+ else
21029+ {
21030+ // Since the call is the only consumer of the box, we know the box can't escape
21031+ // since it is being passed an interior pointer.
21032+ //
21033+ // So, revise the box to simply create a local copy, use the address of that copy
21034+ // as the this pointer, and update the entry point to the unboxed entry.
21035+ //
21036+ // Ideally, we then inline the boxed method and and if it turns out not to modify
21037+ // the copy, we can undo the copy too.
21038+ if (requiresInstMethodTableArg)
2103521039 {
21036- // If that worked, turn the box into a copy to a local var
21040+ // Perform a trial box removal and ask for the type handle tree that fed the box.
2103721041 //
21038- JITDUMP("Found suitable method table arg tree [%06u]\n", dspTreeID(methodTableArg));
21039- GenTree* localCopyThis = gtTryRemoveBoxUpstreamEffects(thisObj, BR_MAKE_LOCAL_COPY);
21042+ JITDUMP("Unboxed entry needs method table arg...\n");
21043+ GenTree* methodTableArg =
21044+ gtTryRemoveBoxUpstreamEffects(thisObj, BR_DONT_REMOVE_WANT_TYPE_HANDLE);
2104021045
21041- if (localCopyThis != nullptr)
21046+ if (methodTableArg != nullptr)
2104221047 {
21043- // Pass the local var as this and the type handle as a new arg
21048+ // If that worked, turn the box into a copy to a local var
2104421049 //
21045- JITDUMP(
21046- "Success! invoking unboxed entry point on local copy, and passing method table arg\n");
21047- call->gtCallThisArg = gtNewCallArgs(localCopyThis);
21048- call->gtCallMoreFlags |= GTF_CALL_M_UNBOXED;
21050+ JITDUMP("Found suitable method table arg tree [%06u]\n", dspTreeID(methodTableArg));
21051+ GenTree* localCopyThis = gtTryRemoveBoxUpstreamEffects(thisObj, BR_MAKE_LOCAL_COPY);
2104921052
21050- // Prepend for R2L arg passing or empty L2R passing
21051- // Append for non-empty L2R
21052- //
21053- if ((Target::g_tgtArgOrder == Target::ARG_ORDER_R2L) || (call->gtCallArgs == nullptr))
21054- {
21055- call->gtCallArgs = gtPrependNewCallArg(methodTableArg, call->gtCallArgs);
21056- }
21057- else
21053+ if (localCopyThis != nullptr)
2105821054 {
21059- GenTreeCall::Use* beforeArg = call->gtCallArgs;
21060- while (beforeArg->GetNext() != nullptr)
21055+ // Pass the local var as this and the type handle as a new arg
21056+ //
21057+ JITDUMP("Success! invoking unboxed entry point on local copy, and passing method table "
21058+ "arg\n");
21059+ call->gtCallThisArg = gtNewCallArgs(localCopyThis);
21060+ call->gtCallMoreFlags |= GTF_CALL_M_UNBOXED;
21061+
21062+ // Prepend for R2L arg passing or empty L2R passing
21063+ // Append for non-empty L2R
21064+ //
21065+ if ((Target::g_tgtArgOrder == Target::ARG_ORDER_R2L) || (call->gtCallArgs == nullptr))
2106121066 {
21062- beforeArg = beforeArg->GetNext( );
21067+ call->gtCallArgs = gtPrependNewCallArg(methodTableArg, call->gtCallArgs );
2106321068 }
21069+ else
21070+ {
21071+ GenTreeCall::Use* beforeArg = call->gtCallArgs;
21072+ while (beforeArg->GetNext() != nullptr)
21073+ {
21074+ beforeArg = beforeArg->GetNext();
21075+ }
2106421076
21065- beforeArg->SetNext(gtNewCallArgs(methodTableArg));
21066- }
21077+ beforeArg->SetNext(gtNewCallArgs(methodTableArg));
21078+ }
2106721079
21068- call->gtCallMethHnd = unboxedEntryMethod;
21069- derivedMethod = unboxedEntryMethod;
21080+ call->gtCallMethHnd = unboxedEntryMethod;
21081+ derivedMethod = unboxedEntryMethod;
2107021082
21071- // Method attributes will differ because unboxed entry point is shared
21072- //
21073- const DWORD unboxedMethodAttribs = info.compCompHnd->getMethodAttribs(unboxedEntryMethod);
21074- JITDUMP("Updating method attribs from 0x%08x to 0x%08x\n", derivedMethodAttribs,
21075- unboxedMethodAttribs);
21076- derivedMethodAttribs = unboxedMethodAttribs;
21077- optimizedTheBox = true;
21083+ // Method attributes will differ because unboxed entry point is shared
21084+ //
21085+ const DWORD unboxedMethodAttribs =
21086+ info.compCompHnd->getMethodAttribs(unboxedEntryMethod);
21087+ JITDUMP("Updating method attribs from 0x%08x to 0x%08x\n", derivedMethodAttribs,
21088+ unboxedMethodAttribs);
21089+ derivedMethodAttribs = unboxedMethodAttribs;
21090+ didOptimize = true;
21091+ }
21092+ else
21093+ {
21094+ JITDUMP("Sorry, failed to undo the box -- can't convert to local copy\n");
21095+ }
2107821096 }
2107921097 else
2108021098 {
21081- JITDUMP("Sorry, failed to undo the box -- can't convert to local copy \n");
21099+ JITDUMP("Sorry, failed to undo the box -- can't find method table arg \n");
2108221100 }
2108321101 }
2108421102 else
2108521103 {
21086- JITDUMP("Sorry, failed to undo the box -- can't find method table arg\n");
21087- }
21088- }
21089- else
21090- {
21091- JITDUMP("Found unboxed entry point, trying to simplify box to a local copy\n");
21092- GenTree* localCopyThis = gtTryRemoveBoxUpstreamEffects(thisObj, BR_MAKE_LOCAL_COPY);
21093-
21094- if (localCopyThis != nullptr)
21095- {
21096- JITDUMP("Success! invoking unboxed entry point on local copy\n");
21097- call->gtCallThisArg = gtNewCallArgs(localCopyThis);
21098- call->gtCallMethHnd = unboxedEntryMethod;
21099- call->gtCallMoreFlags |= GTF_CALL_M_UNBOXED;
21100- derivedMethod = unboxedEntryMethod;
21104+ JITDUMP("Found unboxed entry point, trying to simplify box to a local copy\n");
21105+ GenTree* localCopyThis = gtTryRemoveBoxUpstreamEffects(thisObj, BR_MAKE_LOCAL_COPY);
2110121106
21102- optimizedTheBox = true;
21103- }
21104- else
21105- {
21106- JITDUMP("Sorry, failed to undo the box\n");
21107+ if (localCopyThis != nullptr)
21108+ {
21109+ JITDUMP("Success! invoking unboxed entry point on local copy\n");
21110+ call->gtCallThisArg = gtNewCallArgs(localCopyThis);
21111+ call->gtCallMethHnd = unboxedEntryMethod;
21112+ call->gtCallMoreFlags |= GTF_CALL_M_UNBOXED;
21113+ derivedMethod = unboxedEntryMethod;
21114+ didOptimize = true;
21115+ }
21116+ else
21117+ {
21118+ JITDUMP("Sorry, failed to undo the box\n");
21119+ }
2110721120 }
2110821121 }
2110921122
21110- if (optimizedTheBox )
21123+ if (didOptimize )
2111121124 {
2111221125
2111321126#if FEATURE_TAILCALL_OPT
@@ -21124,7 +21137,7 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call,
2112421137 }
2112521138 }
2112621139
21127- if (!optimizedTheBox )
21140+ if (canOptimize && !didOptimize )
2112821141 {
2112921142 // If we get here, we have a boxed value class that either wasn't boxed
2113021143 // locally, or was boxed locally but we were unable to remove the box for
0 commit comments