Skip to content
10 changes: 7 additions & 3 deletions src/coreclr/jit/importercalls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -566,9 +566,6 @@ var_types Compiler::impImportCall(OPCODE opcode,

if (needsFatPointerHandling)
{
const unsigned fptrLclNum = lvaGrabTemp(true DEBUGARG("fat pointer temp"));
impStoreToTemp(fptrLclNum, fptr, CHECK_SPILL_ALL);
call->AsCall()->gtControlExpr = gtNewLclvNode(fptrLclNum, genActualType(fptr->TypeGet()));
addFatPointerCandidate(call->AsCall());
}
#ifdef FEATURE_READYTORUN
Expand Down Expand Up @@ -8013,6 +8010,12 @@ void Compiler::considerGuardedDevirtualization(GenTreeCall* call,
{
JITDUMP("Considering guarded devirtualization at IL offset %u (0x%x)\n", ilOffset, ilOffset);

if (call->IsGenericVirtual(this))
{
JITDUMP("Generic virtual methods are not supported by guarded devirtualization, sorry.\n");
return;
}

bool hasPgoData = true;

CORINFO_CLASS_HANDLE likelyClasses[MAX_GDV_TYPE_CHECKS] = {};
Expand Down Expand Up @@ -9495,6 +9498,7 @@ void Compiler::impTransformDevirtualizedCall(GenTreeCall* call,
Metrics.DevirtualizedCall++;

// Make the updates.
call->ClearFatPointerCandidate();
call->gtFlags &= ~GTF_CALL_VIRT_VTABLE;
call->gtFlags &= ~GTF_CALL_VIRT_STUB;
call->gtCallMethHnd = derivedMethod;
Expand Down
143 changes: 80 additions & 63 deletions src/coreclr/jit/indirectcalltransformer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,79 @@ class IndirectCallTransformer
virtual GenTreeCall* GetCall(Statement* callStmt) = 0;
virtual void FixupRetExpr() = 0;

//------------------------------------------------------------------------
// SplitCall: spill all side effect uses of the call and the useToSpill into temps.
//
// Parameters
// block - the block to insert the spill statements into.
// useToSpill - the use of the call to spill into a temp.
//
void SplitCall(BasicBlock* block, GenTree** useToSpill)
{
// Find last arg with a side effect. All args with any effect
// before that will need to be spilled.
GenTree** lastSideEffectUse = nullptr;
for (GenTree** use : m_origCall->UseEdges())
{
if (((*use)->gtFlags & GTF_SIDE_EFFECT) != 0)
{
lastSideEffectUse = use;
}
}

if (lastSideEffectUse != nullptr)
{
for (GenTree** use : m_origCall->UseEdges())
{
GenTree* node = *use;
if (((node->gtFlags & GTF_ALL_EFFECT) != 0) || m_compiler->gtHasLocalsWithAddrOp(node))
{
SpillUseToTemp(block, use);
}

if (use == lastSideEffectUse)
{
break;
}
}
}

// We spill the use if it is complex, regardless of side effects.
if (!(*useToSpill)->IsLocal())
{
SpillUseToTemp(block, useToSpill);
}
}

//------------------------------------------------------------------------
// SpillUseToTemp: spill an argument into a temp.
//
// Parameters
// arg - The arg to create a temp and local store for.
//
void SpillUseToTemp(BasicBlock* block, GenTree** use)
{
unsigned tmpNum = m_compiler->lvaGrabTemp(true DEBUGARG("indirect call transform spill temp"));
GenTree* const node = *use;
GenTree* store = m_compiler->gtNewTempStore(tmpNum, node);

if (node->TypeIs(TYP_REF))
{
bool isExact = false;
bool isNonNull = false;
CORINFO_CLASS_HANDLE cls = m_compiler->gtGetClassHandle(node, &isExact, &isNonNull);
if (cls != NO_CLASS_HANDLE)
{
m_compiler->lvaSetClass(tmpNum, cls, isExact);
}
}

Statement* storeStmt = m_compiler->fgNewStmtFromTree(store, m_stmt->GetDebugInfo());
m_compiler->fgInsertStmtAtEnd(block, storeStmt);

*use = m_compiler->gtNewLclVarNode(tmpNum);
}

//------------------------------------------------------------------------
// CreateRemainder: split current block at the call stmt and
// insert statements after the call into m_remainderBlock.
Expand Down Expand Up @@ -379,6 +452,12 @@ class IndirectCallTransformer
{
assert(checkIdx == 0);

if (m_origCall->IsGenericVirtual(m_compiler))
{
SplitCall(m_currBlock, &m_origCall->gtControlExpr);
m_fptrAddress = m_origCall->gtControlExpr;
}

m_checkBlock = CreateAndInsertBasicBlock(BBJ_ALWAYS, m_currBlock, m_currBlock);
GenTree* fatPointerMask = new (m_compiler, GT_CNS_INT) GenTreeIntCon(TYP_I_IMPL, FAT_POINTER_MASK);
GenTree* fptrAddressCopy = m_compiler->gtCloneExpr(m_fptrAddress);
Expand Down Expand Up @@ -630,41 +709,8 @@ class IndirectCallTransformer
prevCheckBlock->SetCond(prevCheckCheckEdge, prevCheckThenEdge);
}

// Find last arg with a side effect. All args with any effect
// before that will need to be spilled.
CallArg* lastSideEffArg = nullptr;
for (CallArg& arg : m_origCall->gtArgs.Args())
{
if ((arg.GetNode()->gtFlags & GTF_SIDE_EFFECT) != 0)
{
lastSideEffArg = &arg;
}
}

if (lastSideEffArg != nullptr)
{
for (CallArg& arg : m_origCall->gtArgs.Args())
{
GenTree* argNode = arg.GetNode();
if (((argNode->gtFlags & GTF_ALL_EFFECT) != 0) || m_compiler->gtHasLocalsWithAddrOp(argNode))
{
SpillArgToTempBeforeGuard(&arg);
}

if (&arg == lastSideEffArg)
{
break;
}
}
}

CallArg* thisArg = m_origCall->gtArgs.GetThisArg();
// We spill 'this' if it is complex, regardless of side effects. It
// is going to be used multiple times due to the guard.
if (!thisArg->GetNode()->IsLocal())
{
SpillArgToTempBeforeGuard(thisArg);
}
SplitCall(m_checkBlock, &thisArg->EarlyNodeRef());

GenTree* thisTree = m_compiler->gtCloneExpr(thisArg->GetNode());

Expand Down Expand Up @@ -743,35 +789,6 @@ class IndirectCallTransformer
m_compiler->fgInsertStmtAtEnd(m_checkBlock, jmpStmt);
}

//------------------------------------------------------------------------
// SpillArgToTempBeforeGuard: spill an argument into a temp in the guard/check block.
//
// Parameters
// arg - The arg to create a temp and local store for.
//
void SpillArgToTempBeforeGuard(CallArg* arg)
{
unsigned tmpNum = m_compiler->lvaGrabTemp(true DEBUGARG("guarded devirt arg temp"));
GenTree* const argNode = arg->GetNode();
GenTree* store = m_compiler->gtNewTempStore(tmpNum, argNode);

if (argNode->TypeIs(TYP_REF))
{
bool isExact = false;
bool isNonNull = false;
CORINFO_CLASS_HANDLE cls = m_compiler->gtGetClassHandle(argNode, &isExact, &isNonNull);
if (cls != NO_CLASS_HANDLE)
{
m_compiler->lvaSetClass(tmpNum, cls, isExact);
}
}

Statement* storeStmt = m_compiler->fgNewStmtFromTree(store, m_stmt->GetDebugInfo());
m_compiler->fgInsertStmtAtEnd(m_checkBlock, storeStmt);

arg->SetEarlyNode(m_compiler->gtNewLclVarNode(tmpNum));
}

//------------------------------------------------------------------------
// FixupRetExpr: set up to repair return value placeholder from call
//
Expand Down
12 changes: 7 additions & 5 deletions src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1556,18 +1556,21 @@ private bool resolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO* info)

if (requiresInstMethodDescArg)
{
#if READYTORUN
if (unboxingStub)
{
// Bail out for now. We need an unboxing stub that points to an instantiated method.
// We need an unboxing stub that points to an instantiated method but this is not happening in R2R.
info->detail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_CANON;
return false;
}
#if READYTORUN

MethodWithToken originalImplWithToken = new MethodWithToken(originalImpl, methodWithTokenImpl.Token, null, false, null, null);
info->instParamLookup.constLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.CreateReadyToRunHelper(ReadyToRunHelperId.MethodDictionary, originalImplWithToken));

#else
info->instParamLookup.constLookup = CreateConstLookupToSymbol(_compilation.NodeFactory.MethodGenericDictionary(originalImpl));
// We could produce a method generic dictionary constant lookup for originalImpl,
// but due to IL scanner limitations, we cannot devirtualize shared generic virtual methods right now.
info->detail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_CANON;
return false;
#endif
}
else if (requiresInstMethodTableArg)
Expand All @@ -1576,7 +1579,6 @@ private bool resolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO* info)
{
#if READYTORUN
info->instParamLookup.constLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.CreateReadyToRunHelper(ReadyToRunHelperId.TypeDictionary, originalImpl.OwningType));

#else
info->instParamLookup.constLookup = CreateConstLookupToSymbol(_compilation.NodeFactory.ConstructedTypeSymbol(originalImpl.OwningType));
#endif
Expand Down
Loading