@@ -400,23 +400,26 @@ var_types Compiler::impImportCall(OPCODE opcode,
400400 thisPtr = impTransformThis (thisPtr, pConstrainedResolvedToken, callInfo->thisTransform );
401401 assert (thisPtr != nullptr );
402402
403+ GenTree* origThisPtr = thisPtr;
403404 // Clone the (possibly transformed) "this" pointer
404405 GenTree* thisPtrCopy;
405406 thisPtr =
406407 impCloneExpr (thisPtr, &thisPtrCopy, CHECK_SPILL_ALL, nullptr DEBUGARG (" LDVIRTFTN this pointer" ));
407408
409+ // We cloned the "this" pointer, mark it as a single def and set the class for it
410+ if (thisPtr->TypeIs (TYP_REF) && (origThisPtr != thisPtr))
411+ {
412+ lvaGetDesc (thisPtr->AsLclVar ())->lvSingleDef = 1 ;
413+ lvaSetClass (thisPtr->AsLclVar ()->GetLclNum (), origThisPtr);
414+ }
415+
408416 GenTree* fptr = impImportLdvirtftn (thisPtr, pResolvedToken, callInfo);
409417 assert (fptr != nullptr );
410418
411419 call->AsCall ()
412420 ->gtArgs .PushFront (this , NewCallArg::Primitive (thisPtrCopy).WellKnown (WellKnownArg::ThisPointer));
413421
414422 // Now make an indirect call through the function pointer
415-
416- unsigned lclNum = lvaGrabTemp (true DEBUGARG (" VirtualCall through function pointer" ));
417- impStoreToTemp (lclNum, fptr, CHECK_SPILL_ALL);
418- fptr = gtNewLclvNode (lclNum, TYP_I_IMPL);
419-
420423 call->AsCall ()->gtCallAddr = fptr;
421424 call->gtFlags |= GTF_EXCEPT | (fptr->gtFlags & GTF_GLOB_EFFECT);
422425
@@ -7603,7 +7606,7 @@ void Compiler::considerGuardedDevirtualization(GenTreeCall* call,
76037606 }
76047607
76057608 addGuardedDevirtualizationCandidate (call, exactMethod, exactCls, exactContext, exactMethodAttrs,
7606- clsAttrs, likelyHood, dvInfo.wasArrayInterfaceDevirt ,
7609+ clsAttrs, likelyHood, dvInfo.needsMethodContext ,
76077610 dvInfo.isInstantiatingStub , baseMethod, originalContext);
76087611 }
76097612
@@ -7676,7 +7679,7 @@ void Compiler::considerGuardedDevirtualization(GenTreeCall* call,
76767679
76777680 likelyContext = dvInfo.exactContext ;
76787681 likelyMethod = dvInfo.devirtualizedMethod ;
7679- arrayInterface = dvInfo.wasArrayInterfaceDevirt ;
7682+ arrayInterface = dvInfo.needsMethodContext ;
76807683 instantiatingStub = dvInfo.isInstantiatingStub ;
76817684 }
76827685 else
@@ -8767,14 +8770,14 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call,
87678770
87688771 if (((size_t )exactContext & CORINFO_CONTEXTFLAGS_MASK) == CORINFO_CONTEXTFLAGS_CLASS)
87698772 {
8770- assert (!dvInfo.wasArrayInterfaceDevirt );
8773+ assert (!dvInfo.needsMethodContext );
87718774 derivedClass = (CORINFO_CLASS_HANDLE)((size_t )exactContext & ~CORINFO_CONTEXTFLAGS_MASK);
87728775 }
87738776 else
87748777 {
87758778 // Array interface devirt can return a nonvirtual generic method of the non-generic SZArrayHelper class.
87768779 //
8777- assert (dvInfo.wasArrayInterfaceDevirt );
8780+ assert (dvInfo.needsMethodContext );
87788781 assert (((size_t )exactContext & CORINFO_CONTEXTFLAGS_MASK) == CORINFO_CONTEXTFLAGS_METHOD);
87798782 derivedClass = info.compCompHnd ->getMethodClass (derivedMethod);
87808783 }
@@ -8795,9 +8798,9 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call,
87958798
87968799 if (dvInfo.isInstantiatingStub )
87978800 {
8798- // We should only end up with generic methods for array interface devirt .
8801+ // We should only end up with generic methods that needs a method context (eg. array interface) .
87998802 //
8800- assert (dvInfo.wasArrayInterfaceDevirt );
8803+ assert (dvInfo.needsMethodContext );
88018804
88028805 // We don't expect NAOT to end up here, since it has Array<T>
88038806 // and normal devirtualization.
@@ -8920,14 +8923,6 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call,
89208923
89218924 JITDUMP (" %s; can devirtualize\n " , note);
89228925
8923- // Make the updates.
8924- call->gtFlags &= ~GTF_CALL_VIRT_VTABLE;
8925- call->gtFlags &= ~GTF_CALL_VIRT_STUB;
8926- call->gtCallMethHnd = derivedMethod;
8927- call->gtCallType = CT_USER_FUNC;
8928- call->gtControlExpr = nullptr ;
8929- INDEBUG (call->gtCallDebugFlags |= GTF_CALL_MD_DEVIRTUALIZED);
8930-
89318926 if (dvInfo.isInstantiatingStub )
89328927 {
89338928 // Pass the instantiating stub method desc as the inst param arg.
@@ -8939,6 +8934,14 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call,
89398934 call->gtArgs .InsertInstParam (this , instParam);
89408935 }
89418936
8937+ // Make the updates.
8938+ call->gtFlags &= ~GTF_CALL_VIRT_VTABLE;
8939+ call->gtFlags &= ~GTF_CALL_VIRT_STUB;
8940+ call->gtCallMethHnd = derivedMethod;
8941+ call->gtCallType = CT_USER_FUNC;
8942+ call->gtControlExpr = nullptr ;
8943+ INDEBUG (call->gtCallDebugFlags |= GTF_CALL_MD_DEVIRTUALIZED);
8944+
89428945 // Virtual calls include an implicit null check, which we may
89438946 // now need to make explicit.
89448947 if (!objIsNonNull)
0 commit comments