@@ -9284,189 +9284,67 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call,
92849284
92859285 if (unboxedEntryMethod != nullptr )
92869286 {
9287- bool optimizedTheBox = false ;
9288-
9289- // If the 'this' object is a local box, see if we can revise things
9290- // to not require boxing .
9287+ // Rewrite the call to target the unboxed entry on the box payload. Keep the heap box,
9288+ // since the callee may return an interior managed pointer into it; object stack allocation
9289+ // can later promote the box to the stack when escape analysis proves the receiver does not
9290+ // escape .
92919291 //
9292- if (thisObj-> IsBoxedValue () && !isExplicitTailCall )
9292+ if (requiresInstMethodTableArg )
92939293 {
9294- // Since the call is the only consumer of the box, we know the box can't escape
9295- // since it is being passed an interior pointer.
9296- //
9297- // So, revise the box to simply create a local copy, use the address of that copy
9298- // as the this pointer, and update the entry point to the unboxed entry.
9294+ // Get the method table from the boxed object.
92999295 //
9300- // Ideally, we then inline the boxed method and and if it turns out not to modify
9301- // the copy, we can undo the copy too.
9302- GenTree* localCopyThis = nullptr ;
9296+ // TODO-CallArgs-REVIEW: Use thisObj here? Differs by gtEffectiveVal.
9297+ GenTree* const clonedThisArg = gtClone (thisArg->GetEarlyNode ());
93039298
9304- if (requiresInstMethodTableArg )
9299+ if (clonedThisArg == nullptr )
93059300 {
9306- // Perform a trial box removal and ask for the type handle tree that fed the box.
9307- //
9308- JITDUMP (" Unboxed entry needs method table arg...\n " );
9309- GenTree* methodTableArg =
9310- gtTryRemoveBoxUpstreamEffects (thisObj, BR_DONT_REMOVE_WANT_TYPE_HANDLE);
9311-
9312- if (methodTableArg != nullptr )
9313- {
9314- // If that worked, turn the box into a copy to a local var
9315- //
9316- JITDUMP (" Found suitable method table arg tree [%06u]\n " , dspTreeID (methodTableArg));
9317- localCopyThis = gtTryRemoveBoxUpstreamEffects (thisObj, BR_MAKE_LOCAL_COPY);
9318-
9319- if (localCopyThis != nullptr )
9320- {
9321- // Pass the local var as this and the type handle as a new arg
9322- //
9323- JITDUMP (" Success! invoking unboxed entry point on local copy, and passing method table "
9324- " arg\n " );
9325- // TODO-CallArgs-REVIEW: Might discard commas otherwise?
9326- assert (thisObj == thisArg->GetEarlyNode ());
9327- thisArg->SetEarlyNode (localCopyThis);
9328- INDEBUG (call->gtCallDebugFlags |= GTF_CALL_MD_UNBOXED);
9329-
9330- call->gtArgs .InsertInstParam (this , methodTableArg);
9331-
9332- call->gtCallMethHnd = unboxedEntryMethod;
9333- derivedMethod = unboxedEntryMethod;
9334- pDerivedResolvedToken = &dvInfo.resolvedTokenDevirtualizedUnboxedMethod ;
9335-
9336- // Method attributes will differ because unboxed entry point is shared
9337- //
9338- const DWORD unboxedMethodAttribs =
9339- info.compCompHnd ->getMethodAttribs (unboxedEntryMethod);
9340- JITDUMP (" Updating method attribs from 0x%08x to 0x%08x\n " , derivedMethodAttribs,
9341- unboxedMethodAttribs);
9342- derivedMethodAttribs = unboxedMethodAttribs;
9343- optimizedTheBox = true ;
9344- }
9345- else
9346- {
9347- JITDUMP (" Sorry, failed to undo the box -- can't convert to local copy\n " );
9348- }
9349- }
9350- else
9351- {
9352- JITDUMP (" Sorry, failed to undo the box -- can't find method table arg\n " );
9353- }
9301+ JITDUMP (" unboxed entry needs MT arg, but `this` was too complex to clone. Deferring update.\n " );
93549302 }
93559303 else
93569304 {
9357- JITDUMP (" Found unboxed entry point, trying to simplify box to a local copy\n " );
9358- localCopyThis = gtTryRemoveBoxUpstreamEffects (thisObj, BR_MAKE_LOCAL_COPY);
9359-
9360- if (localCopyThis != nullptr )
9361- {
9362- JITDUMP (" Success! invoking unboxed entry point on local copy\n " );
9363- assert (thisObj == thisArg->GetEarlyNode ());
9364- // TODO-CallArgs-REVIEW: Might discard commas otherwise?
9365- thisArg->SetEarlyNode (localCopyThis);
9366- call->gtCallMethHnd = unboxedEntryMethod;
9367- INDEBUG (call->gtCallDebugFlags |= GTF_CALL_MD_UNBOXED);
9368- derivedMethod = unboxedEntryMethod;
9369- pDerivedResolvedToken = &dvInfo.resolvedTokenDevirtualizedUnboxedMethod ;
9370-
9371- optimizedTheBox = true ;
9372- }
9373- else
9374- {
9375- JITDUMP (" Sorry, failed to undo the box\n " );
9376- }
9377- }
9378-
9379- if (optimizedTheBox)
9380- {
9381- assert (localCopyThis->IsLclVarAddr ());
9382-
9383- // We may end up inlining this call, so the local copy must be marked as "aliased",
9384- // making sure the inlinee importer will know when to spill references to its value.
9385- lvaGetDesc (localCopyThis->AsLclFld ())->lvHasLdAddrOp = true ;
9386- Metrics.DevirtualizedCallRemovedBox ++;
9387- Metrics.DevirtualizedCallUnboxedEntry ++;
9388-
9389- #if FEATURE_TAILCALL_OPT
9390- if (call->IsImplicitTailCall ())
9391- {
9392- JITDUMP (" Clearing the implicit tail call flag\n " );
9305+ JITDUMP (" revising call to invoke unboxed entry with additional method table arg\n " );
93939306
9394- // If set, we clear the implicit tail call flag
9395- // as we just introduced a new address taken local variable
9396- //
9397- call->gtCallMoreFlags &= ~GTF_CALL_M_IMPLICIT_TAILCALL;
9398- }
9399- #endif // FEATURE_TAILCALL_OPT
9400- }
9401- }
9307+ GenTree* const methodTableArg = gtNewMethodTableLookup (clonedThisArg);
94029308
9403- if (!optimizedTheBox)
9404- {
9405- // If we get here, we have a boxed value class that either wasn't boxed
9406- // locally, or was boxed locally but we were unable to remove the box for
9407- // various reasons.
9408- //
9409- // We can still update the call to invoke the unboxed entry, if the
9410- // boxed value is simple.
9411- //
9412- if (requiresInstMethodTableArg)
9413- {
9414- // Get the method table from the boxed object.
9309+ // Update the 'this' pointer to refer to the box payload
94159310 //
9416- // TODO-CallArgs-REVIEW: Use thisObj here? Differs by gtEffectiveVal.
9417- GenTree* const clonedThisArg = gtClone (thisArg->GetEarlyNode ());
9418-
9419- if (clonedThisArg == nullptr )
9420- {
9421- JITDUMP (
9422- " unboxed entry needs MT arg, but `this` was too complex to clone. Deferring update.\n " );
9423- }
9424- else
9425- {
9426- JITDUMP (" revising call to invoke unboxed entry with additional method table arg\n " );
9427-
9428- GenTree* const methodTableArg = gtNewMethodTableLookup (clonedThisArg);
9429-
9430- // Update the 'this' pointer to refer to the box payload
9431- //
9432- GenTree* const payloadOffset = gtNewIconNode (TARGET_POINTER_SIZE, TYP_I_IMPL);
9433- GenTree* const boxPayload =
9434- gtNewOperNode (GT_ADD, TYP_BYREF, thisArg->GetEarlyNode (), payloadOffset);
9435-
9436- assert (thisObj == thisArg->GetEarlyNode ());
9437- thisArg->SetEarlyNode (boxPayload);
9438- call->gtCallMethHnd = unboxedEntryMethod;
9439- INDEBUG (call->gtCallDebugFlags |= GTF_CALL_MD_UNBOXED);
9440-
9441- // Method attributes will differ because unboxed entry point is shared
9442- //
9443- const DWORD unboxedMethodAttribs = info.compCompHnd ->getMethodAttribs (unboxedEntryMethod);
9444- JITDUMP (" Updating method attribs from 0x%08x to 0x%08x\n " , derivedMethodAttribs,
9445- unboxedMethodAttribs);
9446- derivedMethod = unboxedEntryMethod;
9447- pDerivedResolvedToken = &dvInfo.resolvedTokenDevirtualizedUnboxedMethod ;
9448- derivedMethodAttribs = unboxedMethodAttribs;
9449-
9450- call->gtArgs .InsertInstParam (this , methodTableArg);
9451- Metrics.DevirtualizedCallUnboxedEntry ++;
9452- }
9453- }
9454- else
9455- {
9456- JITDUMP (" revising call to invoke unboxed entry\n " );
9457-
94589311 GenTree* const payloadOffset = gtNewIconNode (TARGET_POINTER_SIZE, TYP_I_IMPL);
94599312 GenTree* const boxPayload =
94609313 gtNewOperNode (GT_ADD, TYP_BYREF, thisArg->GetEarlyNode (), payloadOffset);
94619314
9315+ assert (thisObj == thisArg->GetEarlyNode ());
94629316 thisArg->SetEarlyNode (boxPayload);
94639317 call->gtCallMethHnd = unboxedEntryMethod;
94649318 INDEBUG (call->gtCallDebugFlags |= GTF_CALL_MD_UNBOXED);
9319+
9320+ // Method attributes will differ because unboxed entry point is shared
9321+ //
9322+ const DWORD unboxedMethodAttribs = info.compCompHnd ->getMethodAttribs (unboxedEntryMethod);
9323+ JITDUMP (" Updating method attribs from 0x%08x to 0x%08x\n " , derivedMethodAttribs,
9324+ unboxedMethodAttribs);
94659325 derivedMethod = unboxedEntryMethod;
94669326 pDerivedResolvedToken = &dvInfo.resolvedTokenDevirtualizedUnboxedMethod ;
9327+ derivedMethodAttribs = unboxedMethodAttribs;
9328+
9329+ call->gtArgs .InsertInstParam (this , methodTableArg);
94679330 Metrics.DevirtualizedCallUnboxedEntry ++;
94689331 }
94699332 }
9333+ else
9334+ {
9335+ JITDUMP (" revising call to invoke unboxed entry\n " );
9336+
9337+ GenTree* const payloadOffset = gtNewIconNode (TARGET_POINTER_SIZE, TYP_I_IMPL);
9338+ GenTree* const boxPayload =
9339+ gtNewOperNode (GT_ADD, TYP_BYREF, thisArg->GetEarlyNode (), payloadOffset);
9340+
9341+ thisArg->SetEarlyNode (boxPayload);
9342+ call->gtCallMethHnd = unboxedEntryMethod;
9343+ INDEBUG (call->gtCallDebugFlags |= GTF_CALL_MD_UNBOXED);
9344+ derivedMethod = unboxedEntryMethod;
9345+ pDerivedResolvedToken = &dvInfo.resolvedTokenDevirtualizedUnboxedMethod ;
9346+ Metrics.DevirtualizedCallUnboxedEntry ++;
9347+ }
94709348 }
94719349 else
94729350 {
0 commit comments