Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
3 changes: 2 additions & 1 deletion src/coreclr/jit/compiler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
26 changes: 20 additions & 6 deletions src/coreclr/jit/gentree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -10641,7 +10650,7 @@ void GenTreeUseEdgeIterator::AdvanceCall()
assert(call->gtCallType == CT_INDIRECT);

m_advance = &GenTreeUseEdgeIterator::AdvanceCall<CALL_ADDRESS>;
if (call->gtCallCookie != nullptr)
if (!call->IsVirtualStub() && (call->gtCallCookie != nullptr))
{
m_edge = &call->gtCallCookie;
return;
Expand Down Expand Up @@ -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
Expand Down
16 changes: 14 additions & 2 deletions src/coreclr/jit/importer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6935,8 +6935,20 @@ void Compiler::impImportBlockCode(BasicBlock* block)
{
JITDUMP(".... checking for GDV of IEnumerable<T>...\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<T> check for [%06u]\n", dspTreeID(call));
}

if (ni == NI_System_Collections_Generic_IEnumerable_GetEnumerator)
{
Expand Down
26 changes: 15 additions & 11 deletions src/coreclr/jit/importercalls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -7695,20 +7696,28 @@ void Compiler::impMarkInlineCandidateHelper(GenTreeCall* call,
}
}

/* Ignore helper calls */

// Ignore helper calls
//
if (call->IsHelperCall())
{
assert(!call->IsGuardedDevirtualizationCandidate());
inlineResult->NoteFatal(InlineObservation::CALLSITE_IS_CALL_TO_HELPER);
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).
Expand Down Expand Up @@ -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;
Expand Down
4 changes: 1 addition & 3 deletions src/coreclr/jit/inline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/jit/morph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());

Expand Down
35 changes: 26 additions & 9 deletions src/coreclr/vm/jitinterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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())
{
Expand Down
Loading