diff --git a/src/inc/corjit.h b/src/inc/corjit.h index 86dd32ae54f3..d0236cda5835 100644 --- a/src/inc/corjit.h +++ b/src/inc/corjit.h @@ -85,7 +85,7 @@ enum CorJitFlag CORJIT_FLG_GCPOLL_CALLS = 0x00000040, // Emit calls to JIT_POLLGC for thread suspension. CORJIT_FLG_MCJIT_BACKGROUND = 0x00000080, // Calling from multicore JIT background thread, do not call JitComplete -#ifdef FEATURE_LEGACYNETCF +#if defined(FEATURE_LEGACYNETCF) CORJIT_FLG_NETCF_QUIRKS = 0x00000100, // Mimic .NetCF JIT's quirks for generated code (currently just inlining heuristics) @@ -121,27 +121,33 @@ enum CorJitFlag #endif // !defined(_TARGET_X86_) && !defined(_TARGET_AMD64_) -#ifdef MDIL +#if defined(MDIL) + CORJIT_FLG_MDIL = 0x00004000, // Generate MDIL code instead of machine code -#else // MDIL - CORJIT_FLG_CFI_UNWIND = 0x00004000, // Emit CFI unwind info -#endif // MDIL -#ifdef MDIL // Safe to overlap with CORJIT_FLG_MAKEFINALCODE below. Not used by the JIT, used internally by NGen only. CORJIT_FLG_MINIMAL_MDIL = 0x00008000, // Generate MDIL code suitable for use to bind other assemblies. // Safe to overlap with CORJIT_FLG_READYTORUN below. Not used by the JIT, used internally by NGen only. CORJIT_FLG_NO_MDIL = 0x00010000, // Generate an MDIL section but no code or CTL. Not used by the JIT, used internally by NGen only. -#endif // MDIL + +#else // defined(MDIL) + + CORJIT_FLG_CFI_UNWIND = 0x00004000, // Emit CFI unwind info #if defined(FEATURE_INTERPRETER) + CORJIT_FLG_MAKEFINALCODE = 0x00008000, // Use the final code generator, i.e., not the interpreter. -#endif // FEATURE_INTERPRETER -#ifdef FEATURE_READYTORUN_COMPILER +#endif // defined(FEATURE_INTERPRETER) + +#if defined(FEATURE_READYTORUN_COMPILER) + CORJIT_FLG_READYTORUN = 0x00010000, // Use version-resilient code generation -#endif + +#endif // defined(FEATURE_READYTORUN_COMPILER) + +#endif // !defined(MDIL) CORJIT_FLG_PROF_ENTERLEAVE = 0x00020000, // Instrument prologues/epilogues CORJIT_FLG_PROF_REJIT_NOPS = 0x00040000, // Insert NOPs to ensure code is re-jitable diff --git a/src/jit/codegencommon.cpp b/src/jit/codegencommon.cpp index 463a0a1e30e6..2d0660108aab 100644 --- a/src/jit/codegencommon.cpp +++ b/src/jit/codegencommon.cpp @@ -7558,6 +7558,7 @@ void CodeGen::genPrologPadForReJit() regMaskTP CodeGen::genPInvokeMethodProlog(regMaskTP initRegs) { assert(compiler->compGeneratingProlog); + noway_assert(!compiler->opts.ShouldUsePInvokeHelpers()); noway_assert(compiler->info.compCallUnmanaged); CORINFO_EE_INFO * pInfo = compiler->eeGetEEInfo(); @@ -7777,6 +7778,7 @@ regMaskTP CodeGen::genPInvokeMethodProlog(regMaskTP initRegs) void CodeGen::genPInvokeMethodEpilog() { noway_assert(compiler->info.compCallUnmanaged); + noway_assert(!compiler->opts.ShouldUsePInvokeHelpers()); noway_assert(compiler->compCurBB == compiler->genReturnBB || (compiler->compTailCallUsed && (compiler->compCurBB->bbJumpKind == BBJ_THROW)) || (compiler->compJmpOpUsed && (compiler->compCurBB->bbFlags & BBF_HAS_JMP))); @@ -8518,13 +8520,23 @@ void CodeGen::genFnProlog() // since they are trashed by the jithelper call to setup the PINVOKE frame if (compiler->info.compCallUnmanaged) { - excludeMask |= (RBM_PINVOKE_FRAME | RBM_PINVOKE_TCB | RBM_PINVOKE_SCRATCH); + excludeMask |= RBM_PINVOKE_FRAME; - // We also must exclude the register used by compLvFrameListRoot when it is enregistered - // - LclVarDsc * varDsc = &compiler->lvaTable[compiler->info.compLvFrameListRoot]; - if (varDsc->lvRegister) - excludeMask |= genRegMask(varDsc->lvRegNum); + assert((!compiler->opts.ShouldUsePInvokeHelpers()) || (compiler->info.compLvFrameListRoot == BAD_VAR_NUM)); + if (!compiler->opts.ShouldUsePInvokeHelpers()) + { + noway_assert(compiler->info.compLvFrameListRoot < compiler->lvaCount); + + excludeMask |= (RBM_PINVOKE_TCB | RBM_PINVOKE_SCRATCH); + + // We also must exclude the register used by compLvFrameListRoot when it is enregistered + // + LclVarDsc * varDsc = &compiler->lvaTable[compiler->info.compLvFrameListRoot]; + if (varDsc->lvRegister) + { + excludeMask |= genRegMask(varDsc->lvRegNum); + } + } } #endif // INLINE_NDIRECT diff --git a/src/jit/compiler.h b/src/jit/compiler.h index 44f762d2817a..93fe3b91217f 100644 --- a/src/jit/compiler.h +++ b/src/jit/compiler.h @@ -7707,6 +7707,10 @@ public : inline bool IsReadyToRun() { return false; } #endif + // true if we should use the PINVOKE_{BEGIN,END} helpers instead of generating + // PInvoke transitions inline (e.g. when targeting CoreRT). + inline bool ShouldUsePInvokeHelpers() { return (jitFlags->corJitFlags2 & CORJIT_FLG2_USE_PINVOKE_HELPERS) != 0; } + // true if we must generate compatible code with Jit64 quirks inline bool IsJit64Compat() { diff --git a/src/jit/flowgraph.cpp b/src/jit/flowgraph.cpp index b95c429a77ed..a2b2ee710220 100644 --- a/src/jit/flowgraph.cpp +++ b/src/jit/flowgraph.cpp @@ -8215,7 +8215,12 @@ void Compiler::fgAddInternal() #if INLINE_NDIRECT if (info.compCallUnmanaged != 0) { - info.compLvFrameListRoot = lvaGrabTemp(false DEBUGARG("Pinvoke FrameListRoot")); + // The P/Invoke helpers only require a frame variable, so only allocate the + // TCB variable if we're not using them. + if (!opts.ShouldUsePInvokeHelpers()) + { + info.compLvFrameListRoot = lvaGrabTemp(false DEBUGARG("Pinvoke FrameListRoot")); + } lvaInlinedPInvokeFrameVar = lvaGrabTempWithImplicitUse(false DEBUGARG("Pinvoke FrameVar")); @@ -8226,8 +8231,9 @@ void Compiler::fgAddInternal() varDsc->lvExactSize = eeGetEEInfo()->inlinedCallFrameInfo.size; #if FEATURE_FIXED_OUT_ARGS // Grab and reserve space for TCB, Frame regs used in PInvoke epilog to pop the inlined frame. - // See genPInvokeMethodEpilog() for use of the grabbed var. - if (compJmpOpUsed) + // See genPInvokeMethodEpilog() for use of the grabbed var. This is only necessary if we are + // not using the P/Invoke helpers. + if (!opts.ShouldUsePInvokeHelpers() && compJmpOpUsed) { lvaPInvokeFrameRegSaveVar = lvaGrabTempWithImplicitUse(false DEBUGARG("PInvokeFrameRegSave Var")); varDsc = &lvaTable[lvaPInvokeFrameRegSaveVar]; diff --git a/src/jit/gentree.h b/src/jit/gentree.h index 0d1f538f4ffd..4e06bdd9c6ff 100644 --- a/src/jit/gentree.h +++ b/src/jit/gentree.h @@ -2107,7 +2107,7 @@ struct GenTreeLclVar: public GenTreeLclVarCommon DEBUG_ARG(largeNode)), gtLclILoffs(ilOffs) { - assert(OperIsLocal(oper)); + assert(OperIsLocal(oper) || OperIsLocalAddr(oper)); } #if DEBUGGABLE_GENTREE diff --git a/src/jit/lclvars.cpp b/src/jit/lclvars.cpp index f3c7bd1f5714..15bc1a21fbc8 100644 --- a/src/jit/lclvars.cpp +++ b/src/jit/lclvars.cpp @@ -2170,17 +2170,21 @@ void Compiler::lvaDecRefCnts(GenTreePtr tree) if ((tree->gtOper == GT_CALL) && (tree->gtFlags & GTF_CALL_UNMANAGED)) { - /* Get the special variable descriptor */ + assert((!opts.ShouldUsePInvokeHelpers()) || (info.compLvFrameListRoot == BAD_VAR_NUM)); + if (!opts.ShouldUsePInvokeHelpers()) + { + /* Get the special variable descriptor */ - lclNum = info.compLvFrameListRoot; - - noway_assert(lclNum <= lvaCount); - varDsc = lvaTable + lclNum; - - /* Decrement the reference counts twice */ + lclNum = info.compLvFrameListRoot; - varDsc->decRefCnts(compCurBB->getBBWeight(this), this); - varDsc->decRefCnts(compCurBB->getBBWeight(this), this); + noway_assert(lclNum <= lvaCount); + varDsc = lvaTable + lclNum; + + /* Decrement the reference counts twice */ + + varDsc->decRefCnts(compCurBB->getBBWeight(this), this); + varDsc->decRefCnts(compCurBB->getBBWeight(this), this); + } } else { @@ -2225,17 +2229,21 @@ void Compiler::lvaIncRefCnts(GenTreePtr tree) if ((tree->gtOper == GT_CALL) && (tree->gtFlags & GTF_CALL_UNMANAGED)) { - /* Get the special variable descriptor */ + assert((!opts.ShouldUsePInvokeHelpers()) || (info.compLvFrameListRoot == BAD_VAR_NUM)); + if (!opts.ShouldUsePInvokeHelpers()) + { + /* Get the special variable descriptor */ - lclNum = info.compLvFrameListRoot; - - noway_assert(lclNum <= lvaCount); - varDsc = lvaTable + lclNum; - - /* Increment the reference counts twice */ + lclNum = info.compLvFrameListRoot; - varDsc->incRefCnts(compCurBB->getBBWeight(this), this); - varDsc->incRefCnts(compCurBB->getBBWeight(this), this); + noway_assert(lclNum <= lvaCount); + varDsc = lvaTable + lclNum; + + /* Increment the reference counts twice */ + + varDsc->incRefCnts(compCurBB->getBBWeight(this), this); + varDsc->incRefCnts(compCurBB->getBBWeight(this), this); + } } else { @@ -2811,16 +2819,20 @@ void Compiler::lvaMarkLclRefs(GenTreePtr tree) /* Is this a call to unmanaged code ? */ if (tree->gtOper == GT_CALL && tree->gtFlags & GTF_CALL_UNMANAGED) { - /* Get the special variable descriptor */ + assert((!opts.ShouldUsePInvokeHelpers()) || (info.compLvFrameListRoot == BAD_VAR_NUM)); + if (!opts.ShouldUsePInvokeHelpers()) + { + /* Get the special variable descriptor */ - unsigned lclNum = info.compLvFrameListRoot; - - noway_assert(lclNum <= lvaCount); - LclVarDsc * varDsc = lvaTable + lclNum; + unsigned lclNum = info.compLvFrameListRoot; + + noway_assert(lclNum <= lvaCount); + LclVarDsc * varDsc = lvaTable + lclNum; - /* Increment the ref counts twice */ - varDsc->incRefCnts(lvaMarkRefsWeight, this); - varDsc->incRefCnts(lvaMarkRefsWeight, this); + /* Increment the ref counts twice */ + varDsc->incRefCnts(lvaMarkRefsWeight, this); + varDsc->incRefCnts(lvaMarkRefsWeight, this); + } } #endif @@ -3177,15 +3189,19 @@ void Compiler::lvaMarkLocalVars() if (info.compCallUnmanaged != 0) { - noway_assert(info.compLvFrameListRoot >= info.compLocalsCount && - info.compLvFrameListRoot < lvaCount); + assert((!opts.ShouldUsePInvokeHelpers()) || (info.compLvFrameListRoot == BAD_VAR_NUM)); + if (!opts.ShouldUsePInvokeHelpers()) + { + noway_assert(info.compLvFrameListRoot >= info.compLocalsCount && + info.compLvFrameListRoot < lvaCount); - lvaTable[info.compLvFrameListRoot].lvType = TYP_I_IMPL; + lvaTable[info.compLvFrameListRoot].lvType = TYP_I_IMPL; - /* Set the refCnt, it is used in the prolog and return block(s) */ + /* Set the refCnt, it is used in the prolog and return block(s) */ - lvaTable[info.compLvFrameListRoot].lvRefCnt = 2; - lvaTable[info.compLvFrameListRoot].lvRefCntWtd = 2 * BB_UNITY_WEIGHT; + lvaTable[info.compLvFrameListRoot].lvRefCnt = 2; + lvaTable[info.compLvFrameListRoot].lvRefCntWtd = 2 * BB_UNITY_WEIGHT; + } } #endif diff --git a/src/jit/liveness.cpp b/src/jit/liveness.cpp index 14fa51f8d9ce..43eab8270750 100644 --- a/src/jit/liveness.cpp +++ b/src/jit/liveness.cpp @@ -436,19 +436,23 @@ void Compiler::fgPerStatementLocalVarLiveness(GenTreePtr startNode, GenTreePtr a // This ensures that the block->bbVarUse will contain // the FrameRoot local var if is it a tracked variable. - if (tree->gtCall.IsUnmanaged() || (tree->gtCall.IsTailCall() && info.compCallUnmanaged)) + if ((tree->gtCall.IsUnmanaged() || (tree->gtCall.IsTailCall() && info.compCallUnmanaged))) { - /* Get the TCB local and mark it as used */ + assert((!opts.ShouldUsePInvokeHelpers()) || (info.compLvFrameListRoot == BAD_VAR_NUM)); + if (!opts.ShouldUsePInvokeHelpers()) + { + /* Get the TCB local and mark it as used */ - noway_assert(info.compLvFrameListRoot < lvaCount); + noway_assert(info.compLvFrameListRoot < lvaCount); - LclVarDsc* varDsc = &lvaTable[info.compLvFrameListRoot]; + LclVarDsc* varDsc = &lvaTable[info.compLvFrameListRoot]; - if (varDsc->lvTracked) - { - if (!VarSetOps::IsMember(this, fgCurDefSet, varDsc->lvVarIndex)) + if (varDsc->lvTracked) { - VarSetOps::AddElemD(this, fgCurUseSet, varDsc->lvVarIndex); + if (!VarSetOps::IsMember(this, fgCurDefSet, varDsc->lvVarIndex)) + { + VarSetOps::AddElemD(this, fgCurUseSet, varDsc->lvVarIndex); + } } } } @@ -628,17 +632,21 @@ void Compiler::fgPerBlockLocalVarLiveness() /* Get the TCB local and mark it as used */ - if (block->bbJumpKind == BBJ_RETURN && info.compCallUnmanaged) + if (block->bbJumpKind == BBJ_RETURN && info.compCallUnmanaged) { - noway_assert(info.compLvFrameListRoot < lvaCount); + assert((!opts.ShouldUsePInvokeHelpers()) || (info.compLvFrameListRoot == BAD_VAR_NUM)); + if (!opts.ShouldUsePInvokeHelpers()) + { + noway_assert(info.compLvFrameListRoot < lvaCount); - LclVarDsc * varDsc = &lvaTable[info.compLvFrameListRoot]; + LclVarDsc * varDsc = &lvaTable[info.compLvFrameListRoot]; - if (varDsc->lvTracked) - { - if (!VarSetOps::IsMember(this, fgCurDefSet, varDsc->lvVarIndex)) + if (varDsc->lvTracked) { - VarSetOps::AddElemD(this, fgCurUseSet, varDsc->lvVarIndex); + if (!VarSetOps::IsMember(this, fgCurDefSet, varDsc->lvVarIndex)) + { + VarSetOps::AddElemD(this, fgCurUseSet, varDsc->lvVarIndex); + } } } } @@ -1741,21 +1749,25 @@ VARSET_VALRET_TP Compiler::fgComputeLife(VARSET_VALARG_TP lifeArg, if (tree->gtCall.IsTailCall() && info.compCallUnmanaged) { - /* Get the TCB local and make it live */ + assert((!opts.ShouldUsePInvokeHelpers()) || (info.compLvFrameListRoot == BAD_VAR_NUM)); + if (!opts.ShouldUsePInvokeHelpers()) + { + /* Get the TCB local and make it live */ - noway_assert(info.compLvFrameListRoot < lvaCount); + noway_assert(info.compLvFrameListRoot < lvaCount); - LclVarDsc* frameVarDsc = &lvaTable[info.compLvFrameListRoot]; + LclVarDsc* frameVarDsc = &lvaTable[info.compLvFrameListRoot]; - if (frameVarDsc->lvTracked) - { - VARSET_TP VARSET_INIT_NOCOPY(varBit, VarSetOps::MakeSingleton(this, frameVarDsc->lvVarIndex)); + if (frameVarDsc->lvTracked) + { + VARSET_TP VARSET_INIT_NOCOPY(varBit, VarSetOps::MakeSingleton(this, frameVarDsc->lvVarIndex)); - VarSetOps::AddElemD(this, life, frameVarDsc->lvVarIndex); + VarSetOps::AddElemD(this, life, frameVarDsc->lvVarIndex); - /* Record interference with other live variables */ + /* Record interference with other live variables */ - fgMarkIntf(life, varBit); + fgMarkIntf(life, varBit); + } } } @@ -1769,38 +1781,41 @@ VARSET_VALRET_TP Compiler::fgComputeLife(VARSET_VALARG_TP lifeArg, if (tree->gtCall.IsUnmanaged()) { /* Get the TCB local and make it live */ - - noway_assert(info.compLvFrameListRoot < lvaCount); - - LclVarDsc* frameVarDsc = &lvaTable[info.compLvFrameListRoot]; - - if (frameVarDsc->lvTracked) + assert((!opts.ShouldUsePInvokeHelpers()) || (info.compLvFrameListRoot == BAD_VAR_NUM)); + if (!opts.ShouldUsePInvokeHelpers()) { - unsigned varIndex = frameVarDsc->lvVarIndex; - noway_assert(varIndex < lvaTrackedCount); + noway_assert(info.compLvFrameListRoot < lvaCount); - // Is the variable already known to be alive? - // - if (VarSetOps::IsMember(this, life, varIndex)) + LclVarDsc* frameVarDsc = &lvaTable[info.compLvFrameListRoot]; + + if (frameVarDsc->lvTracked) { - // Since we may call this multiple times, clear the GTF_CALL_M_FRAME_VAR_DEATH if set. + unsigned varIndex = frameVarDsc->lvVarIndex; + noway_assert(varIndex < lvaTrackedCount); + + // Is the variable already known to be alive? // - tree->gtCall.gtCallMoreFlags &= ~GTF_CALL_M_FRAME_VAR_DEATH; - } - else - { - // The variable is just coming to life - // Since this is a backwards walk of the trees - // that makes this change in liveness a 'last-use' + if (VarSetOps::IsMember(this, life, varIndex)) + { + // Since we may call this multiple times, clear the GTF_CALL_M_FRAME_VAR_DEATH if set. + // + tree->gtCall.gtCallMoreFlags &= ~GTF_CALL_M_FRAME_VAR_DEATH; + } + else + { + // The variable is just coming to life + // Since this is a backwards walk of the trees + // that makes this change in liveness a 'last-use' + // + VarSetOps::AddElemD(this, life, varIndex); + tree->gtCall.gtCallMoreFlags |= GTF_CALL_M_FRAME_VAR_DEATH; + } + + // Record an interference with the other live variables // - VarSetOps::AddElemD(this, life, varIndex); - tree->gtCall.gtCallMoreFlags |= GTF_CALL_M_FRAME_VAR_DEATH; + VARSET_TP VARSET_INIT_NOCOPY(varBit, VarSetOps::MakeSingleton(this, varIndex)); + fgMarkIntf(life, varBit); } - - // Record an interference with the other live variables - // - VARSET_TP VARSET_INIT_NOCOPY(varBit, VarSetOps::MakeSingleton(this, varIndex)); - fgMarkIntf(life, varBit); } /* Do we have any live variables? */ diff --git a/src/jit/lower.cpp b/src/jit/lower.cpp index 1dca46113e2d..eaa92c3f6131 100644 --- a/src/jit/lower.cpp +++ b/src/jit/lower.cpp @@ -2475,6 +2475,24 @@ void Lowering::InsertPInvokeMethodProlog() noway_assert(comp->info.compCallUnmanaged); noway_assert(comp->lvaInlinedPInvokeFrameVar != BAD_VAR_NUM); + if (comp->opts.ShouldUsePInvokeHelpers()) + { + // Initialize the P/Invoke frame by calling CORINFO_HELP_INIT_PINVOKE_FRAME: + // + // OpaqueFrame opaqueFrame; + // CORINFO_HELP_INIT_PINVOKE_FRAME(&opaqueFrame); + + GenTree* frameAddr = new(comp, GT_LCL_VAR_ADDR) + GenTreeLclVar(GT_LCL_VAR_ADDR, TYP_BYREF, comp->lvaInlinedPInvokeFrameVar, BAD_IL_OFFSET); + + GenTree* helperCall = comp->gtNewHelperCallNode(CORINFO_HELP_INIT_PINVOKE_FRAME, TYP_VOID, 0, comp->gtNewArgList(frameAddr)); + + GenTreeStmt* stmt = LowerMorphAndSeqTree(helperCall); + comp->fgInsertStmtAtBeg(comp->fgFirstBB, stmt); + + return; + } + CORINFO_EE_INFO* pInfo = comp->eeGetEEInfo(); const CORINFO_EE_INFO::InlinedCallFrameInfo& callFrameInfo = pInfo->inlinedCallFrameInfo; @@ -2555,6 +2573,11 @@ void Lowering::InsertPInvokeMethodEpilog(BasicBlock *returnBB assert(returnBB != nullptr); assert(comp->info.compCallUnmanaged); + if (comp->opts.ShouldUsePInvokeHelpers()) + { + return; + } + // Method doing Pinvoke calls has exactly one return block unless it has "jmp" or tail calls. #ifdef DEBUG bool endsWithTailCallOrJmp = false; @@ -2612,14 +2635,6 @@ void Lowering::InsertPInvokeMethodEpilog(BasicBlock *returnBB // field of the frame (methodHandle may be indirected & have a reloc) void Lowering::InsertPInvokeCallProlog(GenTreeCall* call) { - // emit the following sequence - // - // frame.callTarget := methodHandle - // (x86) frame.callSiteTracker = SP - // frame.callSiteReturnAddress = return address - // thread->gcState = 0 - // (non-stub) - update top Frame on TCB - // GenTree* insertBefore = call; if (call->gtCallType == CT_INDIRECT) { @@ -2633,6 +2648,29 @@ void Lowering::InsertPInvokeCallProlog(GenTreeCall* call) noway_assert(comp->lvaInlinedPInvokeFrameVar != BAD_VAR_NUM); + if (comp->opts.ShouldUsePInvokeHelpers()) + { + // First argument is the address of the frame variable. + GenTree* frameAddr = new(comp, GT_LCL_VAR_ADDR) + GenTreeLclVar(GT_LCL_VAR_ADDR, TYP_BYREF, comp->lvaInlinedPInvokeFrameVar, BAD_IL_OFFSET); + + // Insert call to CORINFO_HELP_JIT_PINVOKE_BEGIN + GenTree* helperCall = comp->gtNewHelperCallNode(CORINFO_HELP_JIT_PINVOKE_BEGIN, TYP_VOID, 0, comp->gtNewArgList(frameAddr)); + + comp->fgMorphTree(helperCall); + comp->fgInsertTreeBeforeAsEmbedded(helperCall, insertBefore, comp->compCurStmt->AsStmt(), currBlock); + return; + } + + // emit the following sequence + // + // frame.callTarget := methodHandle + // (x86) frame.callSiteTracker = SP + // frame.callSiteReturnAddress = return address + // thread->gcState = 0 + // (non-stub) - update top Frame on TCB + // + // ---------------------------------------------------------------------------------- // Setup frame.callSiteTarget (what it referred to in the JIT) // The actual field is Frame.m_Datum which has many different uses and meanings. @@ -2751,10 +2789,26 @@ void Lowering::InsertPInvokeCallProlog(GenTreeCall* call) comp->fgInsertTreeBeforeAsEmbedded(storeGCState, insertBefore, comp->compCurStmt->AsStmt(), currBlock); } - // insert the code that goes after every inlined pinvoke call void Lowering::InsertPInvokeCallEpilog(GenTreeCall* call) { + if (comp->opts.ShouldUsePInvokeHelpers()) + { + noway_assert(comp->lvaInlinedPInvokeFrameVar != BAD_VAR_NUM); + + // First argument is the address of the frame variable. + GenTree* frameAddr = new(comp, GT_LCL_VAR) + GenTreeLclVar(GT_LCL_VAR, TYP_BYREF, comp->lvaInlinedPInvokeFrameVar, BAD_IL_OFFSET); + frameAddr->gtOper = GT_LCL_VAR_ADDR; + + // Insert call to CORINFO_HELP_JIT_PINVOKE_END + GenTree* helperCall = comp->gtNewHelperCallNode(CORINFO_HELP_JIT_PINVOKE_END, TYP_VOID, 0, comp->gtNewArgList(frameAddr)); + + comp->fgMorphTree(helperCall); + comp->fgInsertTreeAfterAsEmbedded(helperCall, call, comp->compCurStmt->AsStmt(), currBlock); + return; + } + CORINFO_EE_INFO* pInfo = comp->eeGetEEInfo(); GenTreeStmt* newStmt; GenTreeStmt* topStmt = comp->compCurStmt->AsStmt(); @@ -2794,6 +2848,55 @@ GenTree* Lowering::LowerNonvirtPinvokeCall(GenTreeCall* call) //------------------------------------------------------ // Non-virtual/Indirect calls: PInvoke calls. + // PInvoke lowering varies depending on the flags passed in by the EE. By default, + // GC transitions are generated inline; if CORJIT_FLG2_USE_PINVOKE_HELPERS is specified, + // GC transitions are instead performed using helper calls. Examples of each case are given + // below. Note that the data structure that is used to store information about a call frame + // containing any P/Invoke calls is initialized in the method prolog (see + // InsertPInvokeMethod{Prolog,Epilog} for details). + // + // Inline transitions: + // InlinedCallFrame inlinedCallFrame; + // + // ... + // + // // Set up frame information + // inlinedCallFrame.callTarget = methodHandle; + // inlinedCallFrame.callSiteTracker = SP; (x86 only) + // inlinedCallFrame.callSiteReturnAddress = &label; (the address of the instruction immediately following the call) + // thread->m_pFrame = &inlinedCallFrame; (non-IL-stub only) + // + // // Switch the thread's GC mode to preemptive mode + // thread->m_fPreemptiveGCDisabled = 0; + // + // // Call the unmanged method + // target(); + // + // // Switch the thread's GC mode back to cooperative mode + // thread->m_fPreemptiveGCDisabled = 1; + // + // // Rendezvous with a running collection if necessary + // if (g_TrapReturningThreads) + // RareDisablePreemptiveGC(); + // + // Transistions using helpers: + // + // OpaqueFrame opaqueFrame; + // + // ... + // + // // Call the JIT_PINVOKE_BEGIN helper + // JIT_PINVOKE_BEGIN(&opaqueFrame); + // + // // Call the unmanaged method + // target(); + // + // // Call the JIT_PINVOKE_END helper + // JIT_PINVOKE_END(&opaqueFrame); + // + // Note that the JIT_PINVOKE_{BEGIN.END} helpers currently use the default calling convention for the target platform. + // They may be changed in the near future s.t. they preserve all register values. + GenTree* result = nullptr; void* addr = nullptr; diff --git a/src/jit/regalloc.cpp b/src/jit/regalloc.cpp index 65b487d2aea2..da48a872566b 100644 --- a/src/jit/regalloc.cpp +++ b/src/jit/regalloc.cpp @@ -6438,6 +6438,9 @@ void Compiler::rpPredictRegUse() // it must not be in a register trashed by the callee if (info.compCallUnmanaged != 0) { + assert(!opts.ShouldUsePInvokeHelpers()); + noway_assert(info.compLvFrameListRoot < lvaCount); + LclVarDsc * pinvokeVarDsc = &lvaTable[info.compLvFrameListRoot]; if (pinvokeVarDsc->lvTracked)