diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 4266be5b1207f0..4039fa91696044 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -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 @@ -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] = {}; @@ -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; diff --git a/src/coreclr/jit/indirectcalltransformer.cpp b/src/coreclr/jit/indirectcalltransformer.cpp index 13059d41d766c3..c51ab4d0831295 100644 --- a/src/coreclr/jit/indirectcalltransformer.cpp +++ b/src/coreclr/jit/indirectcalltransformer.cpp @@ -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. @@ -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); @@ -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()); @@ -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 // diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index d354032f657caf..8d052ffa1b9fe8 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -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) @@ -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