diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 3d6cd904ffd399..635dda470dd7e1 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -11888,7 +11888,7 @@ class GenTreeVisitor if (call->gtCallType == CT_INDIRECT) { - if (call->gtCallCookie != nullptr) + if (!call->IsVirtualStub() && (call->gtCallCookie != nullptr)) { result = WalkTree(&call->gtCallCookie, call); if (result == fgWalkResult::WALK_ABORT) diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index e919af1450995e..74c5e2baaf61c4 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -4683,7 +4683,8 @@ void GenTree::VisitOperands(TVisitor visitor) if (call->gtCallType == CT_INDIRECT) { - if ((call->gtCallCookie != nullptr) && (visitor(call->gtCallCookie) == VisitResult::Abort)) + if (!call->IsVirtualStub() && (call->gtCallCookie != nullptr) && + (visitor(call->gtCallCookie) == VisitResult::Abort)) { return; } diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index ede13bb5ee7f3b..3dc4a00604f61f 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -5977,7 +5977,9 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree) if (call->gtCallType == CT_INDIRECT) { // pinvoke-calli cookie is a constant, or constant indirection - assert(call->gtCallCookie == nullptr || call->gtCallCookie->OperIs(GT_CNS_INT, GT_IND)); + // or a non-tree if this is a managed call. + assert(call->IsVirtualStub() || (call->gtCallCookie == nullptr) || + call->gtCallCookie->OperIs(GT_CNS_INT, GT_IND)); GenTree* indirect = call->gtCallAddr; @@ -6725,7 +6727,7 @@ bool GenTree::TryGetUse(GenTree* operand, GenTree*** pUse) } if (call->gtCallType == CT_INDIRECT) { - if (operand == call->gtCallCookie) + if (!call->IsVirtualStub() && (operand == call->gtCallCookie)) { *pUse = &call->gtCallCookie; return true; @@ -9899,16 +9901,23 @@ GenTreeCall* Compiler::gtCloneExprCallHelper(GenTreeCall* tree) /* Copy the union */ if (tree->gtCallType == CT_INDIRECT) { - copy->gtCallCookie = tree->gtCallCookie ? gtCloneExpr(tree->gtCallCookie) : nullptr; - copy->gtCallAddr = tree->gtCallAddr ? gtCloneExpr(tree->gtCallAddr) : nullptr; + if (tree->IsVirtualStub()) + { + copy->gtCallCookie = tree->gtCallCookie; + } + else + { + copy->gtCallCookie = tree->gtCallCookie ? gtCloneExpr(tree->gtCallCookie) : nullptr; + } + copy->gtCallAddr = tree->gtCallAddr ? gtCloneExpr(tree->gtCallAddr) : nullptr; } else { copy->gtCallMethHnd = tree->gtCallMethHnd; copy->gtInlineCandidateInfo = tree->gtInlineCandidateInfo; - copy->gtInlineInfoCount = tree->gtInlineInfoCount; } + copy->gtInlineInfoCount = tree->gtInlineInfoCount; copy->gtLateDevirtualizationInfo = tree->gtLateDevirtualizationInfo; copy->gtCallType = tree->gtCallType; @@ -10641,7 +10650,7 @@ void GenTreeUseEdgeIterator::AdvanceCall() assert(call->gtCallType == CT_INDIRECT); m_advance = &GenTreeUseEdgeIterator::AdvanceCall; - if (call->gtCallCookie != nullptr) + if (!call->IsVirtualStub() && (call->gtCallCookie != nullptr)) { m_edge = &call->gtCallCookie; return; @@ -10964,6 +10973,11 @@ void Compiler::gtDispNodeName(GenTree* tree) } else if (tree->AsCall()->gtCallType == CT_INDIRECT) { + if (tree->AsCall()->IsVirtual()) + { + callType = "CALLV"; + } + ctType = " ind"; } else diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 5b91fcd6df3a6a..660ddae95cc46f 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -6935,8 +6935,20 @@ void Compiler::impImportBlockCode(BasicBlock* block) { JITDUMP(".... checking for GDV of IEnumerable...\n"); - GenTreeCall* const call = op1->AsRetExpr()->gtInlineCandidate; - NamedIntrinsic const ni = lookupNamedIntrinsic(call->gtCallMethHnd); + GenTreeCall* const call = op1->AsRetExpr()->gtInlineCandidate; + NamedIntrinsic ni = NI_Illegal; + + // TODO -- handle CT_INDIRECT virtuals here too + // but we don't have the right method handle + // + if (call->gtCallType == CT_USER_FUNC) + { + ni = lookupNamedIntrinsic(call->gtCallMethHnd); + } + else if (call->IsGuardedDevirtualizationCandidate()) + { + JITDUMP("No GDV IEnumerable check for [%06u]\n", dspTreeID(call)); + } if (ni == NI_System_Collections_Generic_IEnumerable_GetEnumerator) { diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 5622b2ca5835aa..e5e2e828e5e0df 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -7276,7 +7276,8 @@ void Compiler::considerGuardedDevirtualization(GenTreeCall* call, if (!canResolve) { - JITDUMP("Can't figure out which method would be invoked, sorry\n"); + JITDUMP("Can't figure out which method would be invoked, sorry. [%s]\n", + devirtualizationDetailToString(dvInfo.detail)); // Continue checking other candidates, maybe some of them will succeed. break; @@ -7695,8 +7696,8 @@ void Compiler::impMarkInlineCandidateHelper(GenTreeCall* call, } } - /* Ignore helper calls */ - + // Ignore helper calls + // if (call->IsHelperCall()) { assert(!call->IsGuardedDevirtualizationCandidate()); @@ -7704,11 +7705,19 @@ void Compiler::impMarkInlineCandidateHelper(GenTreeCall* call, return; } - /* Ignore indirect calls */ + // Ignore indirect calls, unless they are indirect virtual stub calls with profile info. + // if (call->gtCallType == CT_INDIRECT) { - inlineResult->NoteFatal(InlineObservation::CALLSITE_IS_NOT_DIRECT_MANAGED); - return; + if (!call->IsGuardedDevirtualizationCandidate()) + { + inlineResult->NoteFatal(InlineObservation::CALLSITE_IS_NOT_DIRECT_MANAGED); + return; + } + else + { + assert(call->IsVirtualStub()); + } } // The inliner gets confused when the unmanaged convention reverses arg order (like x86). @@ -8924,11 +8933,6 @@ bool Compiler::impConsiderCallProbe(GenTreeCall* call, IL_OFFSET ilOffset) // Compiler::GDVProbeType Compiler::compClassifyGDVProbeType(GenTreeCall* call) { - if (call->gtCallType == CT_INDIRECT) - { - return GDVProbeType::None; - } - if (!opts.jitFlags->IsSet(JitFlags::JIT_FLAG_BBINSTR) || IsAot()) { return GDVProbeType::None; diff --git a/src/coreclr/jit/inline.cpp b/src/coreclr/jit/inline.cpp index 7dfa4ba474ab30..7c862c8744d130 100644 --- a/src/coreclr/jit/inline.cpp +++ b/src/coreclr/jit/inline.cpp @@ -1344,9 +1344,7 @@ InlineContext* InlineStrategy::NewContext(InlineContext* parentContext, Statemen // which becomes a single statement where the IL location points to the // ldarg instruction. context->m_Location = stmt->GetDebugInfo().GetLocation(); - - assert(call->gtCallType == CT_USER_FUNC); - context->m_Callee = call->gtCallMethHnd; + context->m_Callee = call->gtCallMethHnd; #if defined(DEBUG) context->m_Devirtualized = call->IsDevirtualized(); diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 65f339748c0c22..29beafdb321e85 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -1773,7 +1773,7 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call // add as a non-standard arg. } } - else if (call->gtCallType == CT_INDIRECT && (call->gtCallCookie != nullptr)) + else if ((call->gtCallType == CT_INDIRECT) && !call->IsVirtualStub() && (call->gtCallCookie != nullptr)) { assert(!call->IsUnmanaged()); diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index d55f2969a1fe24..9f4c9a21c78c82 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -8638,7 +8638,7 @@ bool CEEInfo::resolveVirtualMethodHelper(CORINFO_DEVIRTUALIZATION_INFO * info) return false; } } - else if (!pObjMT->CanCastToInterface(pBaseMT)) + else if (!pBaseMT->IsSharedByGenericInstantiations() && !pObjMT->CanCastToInterface(pBaseMT)) { info->detail = CORINFO_DEVIRTUALIZATION_FAILED_CAST; return false; @@ -8648,32 +8648,49 @@ bool CEEInfo::resolveVirtualMethodHelper(CORINFO_DEVIRTUALIZATION_INFO * info) // safely devirtualize. if (info->context != nullptr) { - // If the derived class is a shared class, make sure the - // owner class is too. - if (pObjMT->IsSharedByGenericInstantiations()) + MethodTable* interfaceMT = nullptr; + + if (pObjMT->IsSharedByGenericInstantiations() || pBaseMT->IsSharedByGenericInstantiations()) { MethodTable* pCanonBaseMT = pBaseMT->GetCanonicalMethodTable(); - + // Check to see if the derived class implements multiple variants of a matching interface. // If so, we cannot predict exactly which implementation is in use here. MethodTable::InterfaceMapIterator it = pObjMT->IterateInterfaceMap(); int canonicallyMatchingInterfacesFound = 0; + MethodTable* interfaceMT = nullptr; while (it.Next()) { - if (it.GetInterface(pObjMT)->GetCanonicalMethodTable() == pCanonBaseMT) + MethodTable* mt = it.GetInterface(pObjMT); + if (mt->GetCanonicalMethodTable() == pCanonBaseMT) { + interfaceMT = mt; canonicallyMatchingInterfacesFound++; + if (canonicallyMatchingInterfacesFound > 1) { - // Multiple canonically identical interfaces found when attempting to devirtualize an inexact interface dispatch + // Multiple canonically identical interfaces found. + // info->detail = CORINFO_DEVIRTUALIZATION_MULTIPLE_IMPL; return false; } } } - } + + if (canonicallyMatchingInterfacesFound == 0) + { + // The object doesn't implement the interface... + // + info->detail = CORINFO_DEVIRTUALIZATION_FAILED_CAST; + return false; + } - pDevirtMD = pObjMT->GetMethodDescForInterfaceMethod(TypeHandle(pBaseMT), pBaseMD, FALSE /* throwOnConflict */); + pDevirtMD = pObjMT->GetMethodDescForInterfaceMethod(TypeHandle(interfaceMT), pBaseMD, FALSE /* throwOnConflict */); + } + else + { + pDevirtMD = pObjMT->GetMethodDescForInterfaceMethod(TypeHandle(pBaseMT), pBaseMD, FALSE /* throwOnConflict */); + } } else if (!pBaseMD->HasClassOrMethodInstantiation()) {