diff --git a/docs/design/coreclr/botr/clr-abi.md b/docs/design/coreclr/botr/clr-abi.md index 0585bfee301291..f0510bfa4afeee 100644 --- a/docs/design/coreclr/botr/clr-abi.md +++ b/docs/design/coreclr/botr/clr-abi.md @@ -148,8 +148,6 @@ ARM64-only: When a method returns a structure that is larger than 16 bytes the c *Stub dispatch* - when a virtual call uses a VSD stub, rather than back-patching the calling code (or disassembling it), the JIT must place the address of the stub used to load the call target, the "stub indirection cell", in (x86) `EAX` / (AMD64) `R11` / (ARM) `R12` / (ARM64) `R11`. In the JIT, this is encapsulated in the `VirtualStubParamInfo` class. -*Calli Pinvoke* - The VM wants the address of the PInvoke in (AMD64) `R10` / (ARM) `R12` / (ARM64) `R14` (In the JIT: `REG_PINVOKE_TARGET_PARAM`), and the signature (the pinvoke cookie) in (AMD64) `R11` / (ARM) `R4` / (ARM64) `R15` (in the JIT: `REG_PINVOKE_COOKIE_PARAM`). - *Normal PInvoke* - The VM shares IL stubs based on signatures, but wants the right method to show up in call stack and exceptions, so the MethodDesc for the exact PInvoke is passed in the (x86) `EAX` / (AMD64) `R10` / (ARM, ARM64) `R12` (in the JIT: `REG_SECRET_STUB_PARAM`). Then in the IL stub, when the JIT gets `CORJIT_FLG_PUBLISH_SECRET_PARAM`, it must move the register into a compiler temp. The value is returned for the intrinsic `NI_System_StubHelpers_GetStubContext`. ## Small primitive returns diff --git a/docs/design/coreclr/botr/guide-for-porting.md b/docs/design/coreclr/botr/guide-for-porting.md index ff90ba5c7df419..f569807cb63389 100644 --- a/docs/design/coreclr/botr/guide-for-porting.md +++ b/docs/design/coreclr/botr/guide-for-porting.md @@ -400,10 +400,8 @@ Here is an annotated list of the stubs implemented for Unix on Arm64. for ReadyToRun pre-compiled pinvoke calls, so that they do not cause GC starvation - 15. `VarargPInvokeStub`/ `GenericPInvokeCalliHelper` Used to support calli - pinvokes. It is expected that C\# 8.0 will increase use of this feature. - Today use of this feature on Unix requires hand-written IL. On Windows - this feature is commonly used by C++/CLI + 15. `VarargPInvokeStub` – Used to support vararg pinvokes, which are commonly + used by C++/CLI. Not necessary for non-Windows platforms at this time. #### cgencpu.h diff --git a/src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs b/src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs index 289e5bd0623361..deb663e5a10bd3 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs @@ -2179,6 +2179,9 @@ internal static partial class StubHelpers [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "StubHelpers_ThrowInteropParamException")] internal static safe partial void ThrowInteropParamException(int resID, int paramIdx); + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "StubHelpers_ThrowInteropException")] + internal static partial void ThrowInteropException(int exceptionKind, int resID); + internal static IntPtr AddToCleanupList(ref CleanupWorkListElement? pCleanupWorkList, SafeHandle handle) { SafeHandleCleanupWorkListElement element = new SafeHandleCleanupWorkListElement(handle); diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index f85c006c9a1d1a..2a1a3c6fdb832a 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -481,7 +481,6 @@ enum CorInfoHelpFunc /* Miscellaneous */ - CORINFO_HELP_PINVOKE_CALLI, // Indirect pinvoke call CORINFO_HELP_TAILCALL, // Perform a tail call CORINFO_HELP_GETCURRENTMANAGEDTHREADID, @@ -3384,12 +3383,6 @@ class ICorDynamicInfo : public ICorStaticInfo CORINFO_CONST_LOOKUP * pLookup ) = 0; - // Generate a cookie based on the signature to pass to CORINFO_HELP_PINVOKE_CALLI - virtual void* GetCookieForPInvokeCalliSig( - CORINFO_SIG_INFO* szMetaSig, - void** ppIndirection = NULL - ) = 0; - // Generate a cookie based on the signature to pass to INTOP_CALLI in the interpreter. virtual void* GetCookieForInterpreterCalliSig( CORINFO_SIG_INFO* szMetaSig) = 0; @@ -3533,6 +3526,7 @@ class ICorDynamicInfo : public ICorStaticInfo ) = 0; // Optionally, convert calli to regular method call. This is for PInvoke argument marshalling. + // On success, pResolvedToken->hMethod is set to the method that should be called instead. virtual bool convertPInvokeCalliToCall( CORINFO_RESOLVED_TOKEN * pResolvedToken, bool fMustConvert diff --git a/src/coreclr/inc/icorjitinfoimpl_generated.h b/src/coreclr/inc/icorjitinfoimpl_generated.h index 5a7fb30fe7e14d..7f99b8b87ad0d7 100644 --- a/src/coreclr/inc/icorjitinfoimpl_generated.h +++ b/src/coreclr/inc/icorjitinfoimpl_generated.h @@ -604,10 +604,6 @@ void getAddressOfPInvokeTarget( CORINFO_METHOD_HANDLE method, CORINFO_CONST_LOOKUP* pLookup) override; -void* GetCookieForPInvokeCalliSig( - CORINFO_SIG_INFO* szMetaSig, - void** ppIndirection) override; - void* GetCookieForInterpreterCalliSig( CORINFO_SIG_INFO* szMetaSig) override; diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h index 578c88b80b5252..f2d46fcd173aa3 100644 --- a/src/coreclr/inc/jiteeversionguid.h +++ b/src/coreclr/inc/jiteeversionguid.h @@ -37,11 +37,11 @@ #include -constexpr GUID JITEEVersionIdentifier = { /* 305cdd16-2cee-49af-ae7c-2537eb37cae9 */ - 0x305cdd16, - 0x2cee, - 0x49af, - {0xae, 0x7c, 0x25, 0x37, 0xeb, 0x37, 0xca, 0xe9} +constexpr GUID JITEEVersionIdentifier = { /* 1ba67350-5cd4-47fb-81c6-907234e7dbb6 */ + 0x1ba67350, + 0x5cd4, + 0x47fb, + {0x81, 0xc6, 0x90, 0x72, 0x34, 0xe7, 0xdb, 0xb6} }; #endif // JIT_EE_VERSIONING_GUID_H diff --git a/src/coreclr/inc/jithelpers.h b/src/coreclr/inc/jithelpers.h index 7742945b23e7e4..3ba323077d060d 100644 --- a/src/coreclr/inc/jithelpers.h +++ b/src/coreclr/inc/jithelpers.h @@ -216,8 +216,6 @@ DYNAMICJITHELPER(CORINFO_HELP_PROF_FCN_TAILCALL, JIT_ProfilerEnterLeaveTailcallStub, METHOD__NIL) // Miscellaneous - JITHELPER(CORINFO_HELP_PINVOKE_CALLI, GenericPInvokeCalliHelper, METHOD__NIL) - #if defined(TARGET_X86) && !defined(UNIX_X86_ABI) JITHELPER(CORINFO_HELP_TAILCALL, JIT_TailCall, METHOD__NIL) #else diff --git a/src/coreclr/jit/ICorJitInfo_names_generated.h b/src/coreclr/jit/ICorJitInfo_names_generated.h index f3265a45960177..8bb8a99fd8494f 100644 --- a/src/coreclr/jit/ICorJitInfo_names_generated.h +++ b/src/coreclr/jit/ICorJitInfo_names_generated.h @@ -149,7 +149,6 @@ DEF_CLR_API(embedFieldHandle) DEF_CLR_API(embedGenericHandle) DEF_CLR_API(getLocationOfThisType) DEF_CLR_API(getAddressOfPInvokeTarget) -DEF_CLR_API(GetCookieForPInvokeCalliSig) DEF_CLR_API(GetCookieForInterpreterCalliSig) DEF_CLR_API(getJustMyCodeHandle) DEF_CLR_API(GetProfilingHandle) diff --git a/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp b/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp index 89e8e05606cfbb..b4df7303ea92bf 100644 --- a/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp +++ b/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp @@ -1426,16 +1426,6 @@ void WrapICorJitInfo::getAddressOfPInvokeTarget( API_LEAVE(getAddressOfPInvokeTarget); } -void* WrapICorJitInfo::GetCookieForPInvokeCalliSig( - CORINFO_SIG_INFO* szMetaSig, - void** ppIndirection) -{ - API_ENTER(GetCookieForPInvokeCalliSig); - void* temp = wrapHnd->GetCookieForPInvokeCalliSig(szMetaSig, ppIndirection); - API_LEAVE(GetCookieForPInvokeCalliSig); - return temp; -} - void* WrapICorJitInfo::GetCookieForInterpreterCalliSig( CORINFO_SIG_INFO* szMetaSig) { diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index a84e927a407dd2..132d726d9ee31d 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -9641,8 +9641,6 @@ class Compiler // Get the offset of a MDArray's lower bound for a given dimension. static unsigned eeGetMDArrayLowerBoundOffset(unsigned rank, unsigned dimension); - CORINFO_CONST_LOOKUP eeConvertToLookup(void* value, void* pValue); - // Returns the page size for the target machine as reported by the EE. target_size_t eeGetPageSize() { diff --git a/src/coreclr/jit/ee_il_dll.cpp b/src/coreclr/jit/ee_il_dll.cpp index 47af3c1f9b7e06..f996735d520c6f 100644 --- a/src/coreclr/jit/ee_il_dll.cpp +++ b/src/coreclr/jit/ee_il_dll.cpp @@ -504,34 +504,6 @@ unsigned Compiler::eeGetArgSizeAlignment(var_types type, bool isFloatHfa) } } -//------------------------------------------------------------------------ -// eeConvertToLookup: Convert a tuple of "{ value, pValue }" to "CORINFO_CONST_LOOKUP". -// -// Arguments: -// value - The direct value (IAT_VALUE) -// pValue - The indirect value (IAT_PVALUE) -// -// Return Value: -// The lookup. -// -CORINFO_CONST_LOOKUP Compiler::eeConvertToLookup(void* value, void* pValue) -{ - CORINFO_CONST_LOOKUP lookup; - if (value != nullptr) - { - assert(pValue == nullptr); - lookup.accessType = IAT_VALUE; - lookup.addr = value; - } - else - { - assert(pValue != nullptr); - lookup.accessType = IAT_PVALUE; - lookup.addr = pValue; - } - return lookup; -} - //------------------------------------------------------------------------ // eeGetArrayDataOffset: Gets the offset of a SDArray's first element // diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index e405606544b083..7d64f909262b4b 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -1608,14 +1608,6 @@ bool CallArgs::GetCustomRegister(Compiler* comp, CorInfoCallConvExtension cc, We *reg = comp->virtualStubParamInfo->GetReg(); return true; - case WellKnownArg::PInvokeCookie: - *reg = REG_PINVOKE_COOKIE_PARAM; - return true; - - case WellKnownArg::PInvokeTarget: - *reg = REG_PINVOKE_TARGET_PARAM; - return true; - case WellKnownArg::R2RIndirectionCell: *reg = REG_R2R_INDIRECT_PARAM; return true; @@ -2398,12 +2390,6 @@ int GenTreeCall::GetNonStandardAddedArgCount(Compiler* compiler) const // R11 = Virtual stub param return 1; } - else if ((gtCallType == CT_INDIRECT) && (gtCallCookie != nullptr)) - { - // R10 = PInvoke target param - // R11 = PInvoke cookie param - return 2; - } return 0; } @@ -2621,18 +2607,6 @@ bool GenTreeCall::Equals(GenTreeCall* c1, GenTreeCall* c2) } } } - else if (!c1->IsVirtualStub()) - { - if ((c1->gtCallCookie == nullptr) != (c2->gtCallCookie == nullptr)) - { - return false; - } - - if ((c1->gtCallCookie != nullptr) && !sameLookup(*c1->gtCallCookie, *c2->gtCallCookie)) - { - return false; - } - } auto sameArgMetadata = [](CallArg* a1, CallArg* a2) { return (a1->GetSignatureType() == a2->GetSignatureType()) && @@ -10121,11 +10095,10 @@ GenTreeCall* Compiler::gtNewCallNode(gtCallTypes callType, node->gtRetClsHnd = nullptr; node->gtCallMoreFlags = GTF_CALL_M_EMPTY; INDEBUG(node->gtCallDebugFlags = GTF_CALL_MD_EMPTY); - node->gtInlineInfoCount = 0; + node->ClearInlineInfo(); if (callType == CT_INDIRECT) { - node->gtCallCookie = nullptr; node->gtCallMethHnd = NO_METHOD_HANDLE; node->gtControlExpr = (GenTree*)callHnd; } @@ -10133,7 +10106,6 @@ GenTreeCall* Compiler::gtNewCallNode(gtCallTypes callType, { node->gtCallMethHnd = callHnd; node->gtControlExpr = nullptr; - node->ClearInlineInfo(); } node->gtReturnType = type; @@ -11752,15 +11724,15 @@ GenTreeCall* Compiler::gtCloneExprCallHelper(GenTreeCall* tree) copy->gtStubCallStubAddr = tree->gtStubCallStubAddr; /* Copy the union */ + copy->gtInlineCandidateInfo = tree->gtInlineCandidateInfo; + if (tree->gtCallType == CT_INDIRECT) { - copy->gtCallCookie = tree->gtCallCookie; copy->gtCallMethHnd = NO_METHOD_HANDLE; } else { - copy->gtCallMethHnd = tree->gtCallMethHnd; - copy->gtInlineCandidateInfo = tree->gtInlineCandidateInfo; + copy->gtCallMethHnd = tree->gtCallMethHnd; } copy->gtInlineInfoCount = tree->gtInlineInfoCount; diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index cce0ecb434e2fe..18905a4f700d20 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -5875,9 +5875,6 @@ struct GenTreeCall final : public GenTree union { - // The serialized CALLI unmanaged call (CT_INDIRECT) cookie; reified into argument IR in morph - CORINFO_CONST_LOOKUP* gtCallCookie; - // gtInlineCandidateInfo is only used when inlining methods InlineCandidateInfo* gtInlineCandidateInfo; // gtInlineCandidateInfoList is used when we have more than one GDV candidate diff --git a/src/coreclr/jit/handlekinds.h b/src/coreclr/jit/handlekinds.h index 9516c5193c8baf..d575617302b638 100644 --- a/src/coreclr/jit/handlekinds.h +++ b/src/coreclr/jit/handlekinds.h @@ -16,7 +16,6 @@ HANDLE_KIND(GTF_ICON_OBJ_HDL , "object" , 0) HANDLE_KIND(GTF_ICON_CONST_PTR , "const ptr" , HKF_INVARIANT) // pointer to immutable data, (e.g. IAT_PPVALUE) HANDLE_KIND(GTF_ICON_GLOBAL_PTR , "global ptr" , 0) // pointer to mutable data (e.g. from the VM state) HANDLE_KIND(GTF_ICON_VARG_HDL , "vararg" , HKF_INVARIANT) // var arg cookie handle -HANDLE_KIND(GTF_ICON_PINVKI_HDL , "pinvoke" , 0) // pinvoke calli handle HANDLE_KIND(GTF_ICON_TOKEN_HDL , "token" , HKF_INVARIANT) // token handle (other than class, method or field) HANDLE_KIND(GTF_ICON_TLS_HDL , "tls" , HKF_INVARIANT) // TLS ref with offset HANDLE_KIND(GTF_ICON_FTN_ADDR , "ftn" , 0) // function address diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index a8fbed9a28b72d..4df63847d95a6c 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -339,13 +339,12 @@ var_types Compiler::impImportCall(OPCODE opcode, if (opcode == CEE_CALLI) { - if (IsTargetAbi(CORINFO_NATIVEAOT_ABI)) + if (info.compCompHnd->convertPInvokeCalliToCall(pResolvedToken, !impCanPInvokeInlineCallSite(compCurBB))) { - if (info.compCompHnd->convertPInvokeCalliToCall(pResolvedToken, !impCanPInvokeInlineCallSite(compCurBB))) - { - eeGetCallInfo(pResolvedToken, nullptr, CORINFO_CALLINFO_ALLOWINSTPARAM, callInfo); - return impImportCall(CEE_CALL, pResolvedToken, nullptr, nullptr, prefixFlags, callInfo, rawILOffset); - } + // The VM only fills in hMethod; derive the rest of the token from it. + pResolvedToken->hClass = info.compCompHnd->getMethodClass(pResolvedToken->hMethod); + eeGetCallInfo(pResolvedToken, nullptr, CORINFO_CALLINFO_ALLOWINSTPARAM, callInfo); + return impImportCall(CEE_CALL, pResolvedToken, nullptr, nullptr, prefixFlags, callInfo, rawILOffset); } /* Get the call site sig */ @@ -935,20 +934,6 @@ var_types Compiler::impImportCall(OPCODE opcode, goto DONE; } - else if ((opcode == CEE_CALLI) && ((sig->callConv & CORINFO_CALLCONV_MASK) != CORINFO_CALLCONV_DEFAULT) && - ((sig->callConv & CORINFO_CALLCONV_MASK) != CORINFO_CALLCONV_VARARG)) - { - void* pCookie; - void* cookie = info.compCompHnd->GetCookieForPInvokeCalliSig(sig, &pCookie); - CORINFO_CONST_LOOKUP cookieLookup = eeConvertToLookup(cookie, pCookie); - call->AsCall()->gtCallCookie = new (getAllocator(CMK_ASTNode)) CORINFO_CONST_LOOKUP(cookieLookup); - - if (canTailCall) - { - canTailCall = false; - szCanTailCallFailReason = "PInvoke calli"; - } - } if (sig->isAsyncCall()) { @@ -7301,8 +7286,6 @@ void Compiler::impCheckForPInvokeCall( } unmanagedCallConv = info.compCompHnd->getUnmanagedCallConv(nullptr, sig, &suppressGCTransition); - - assert(!call->gtCallCookie); } if (suppressGCTransition) @@ -7325,13 +7308,11 @@ void Compiler::impCheckForPInvokeCall( } optNativeCallCount++; - if (methHnd == nullptr && (IsTargetAbi(CORINFO_NATIVEAOT_ABI) || - (opts.jitFlags->IsSet(JitFlags::JIT_FLAG_IL_STUB) && !compIsForInlining()))) + if (methHnd == nullptr) { - // PInvoke CALLI in NativeAOT ABI must be always inlined. Non-inlineable CALLI cases have been - // converted to regular method calls earlier using convertPInvokeCalliToCall. - - // PInvoke CALLI in IL stubs must be inlined + // PInvoke CALLI must always be inlined. Call sites that cannot be inlined have been converted + // to a call to a marshalling stub earlier using convertPInvokeCalliToCall, or, in ReadyToRun, + // have aborted the compilation of this method. } else if (opts.jitFlags->IsSet(JitFlags::JIT_FLAG_IL_STUB) && IsReadyToRun()) { @@ -8518,17 +8499,6 @@ void Compiler::addGuardedDevirtualizationCandidate(GenTreeCall* call, return; } - // CT_INDIRECT calls may use the cookie, bail if so... - // - // If transforming these provides a benefit, we could save this off in the same way - // we save the stub address below. - if ((call->gtCallType == CT_INDIRECT) && (call->AsCall()->gtCallCookie != nullptr)) - { - JITDUMP("NOT Marking call [%06u] as guarded devirtualization candidate -- CT_INDIRECT with cookie\n", - dspTreeID(call)); - return; - } - #ifdef DEBUG // See if disabled by range diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 099e0400e1794c..668d434dd5f791 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -2044,7 +2044,10 @@ void Lowering::LowerSpecialCopyArgs(GenTreeCall* call) // which will be first in the list. // The this parameter is always passed in registers, so we can ignore it. unsigned argIndex = call->gtArgs.CountUserArgs() - 1; - assert(call->gtArgs.CountUserArgs() == m_compiler->info.compILargsCount); + // The arguments of the unmanaged call are the leading arguments of the IL stub, so the stub + // cannot have fewer of them. It can have more: an unmanaged CALLI stub takes the call target + // as an extra trailing argument that is not passed on to the unmanaged call. + assert(call->gtArgs.CountUserArgs() <= m_compiler->info.compILargsCount); bool checkForUnmanagedThisArg = call->GetUnmanagedCallConv() == CorInfoCallConvExtension::Thiscall; for (CallArg& arg : call->gtArgs.Args()) { @@ -2903,11 +2906,7 @@ GenTree* Lowering::LowerCall(GenTree* node) { controlExpr = LowerNonvirtPinvokeCall(call); } - else if (call->gtCallType == CT_INDIRECT) - { - controlExpr = LowerIndirectNonvirtCall(call); - } - else + else if (call->gtCallType != CT_INDIRECT) { controlExpr = LowerDirectCall(call); } @@ -6659,14 +6658,6 @@ void Lowering::OptimizeCallIndirectTargetEvaluation(GenTreeCall* call) DISPTREERANGE(BlockRange(), call); } -GenTree* Lowering::LowerIndirectNonvirtCall(GenTreeCall* call) -{ - // Indirect cookie calls gets transformed by fgMorphArgs as indirect call with non-standard args. - // Hence we should never see this type of call in lower. - noway_assert(call->gtCallCookie == nullptr); - return nullptr; -} - //------------------------------------------------------------------------ // CreateReturnTrapSeq: Create a tree to perform a "return trap", used in PInvoke // epilogs to invoke a GC under a condition. The return trap checks some global diff --git a/src/coreclr/jit/lower.h b/src/coreclr/jit/lower.h index bcc17b9ae29630..01348695c23541 100644 --- a/src/coreclr/jit/lower.h +++ b/src/coreclr/jit/lower.h @@ -199,7 +199,6 @@ class Lowering final : public Phase #endif // WINDOWS_AMD64_ABI GenTree* LowerDelegateInvoke(GenTreeCall* call); void OptimizeCallIndirectTargetEvaluation(GenTreeCall* call); - GenTree* LowerIndirectNonvirtCall(GenTreeCall* call); GenTree* LowerDirectCall(GenTreeCall* call); GenTree* LowerNonvirtPinvokeCall(GenTreeCall* call); GenTree* LowerTailCallViaJitHelper(GenTreeCall* callNode, GenTree* callTarget); diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 3f58a31b3f0625..c5305b8bbccf7a 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -1756,34 +1756,6 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call // add as a non-standard arg. } } - else if ((call->gtCallType == CT_INDIRECT) && !call->IsVirtualStub() && (call->gtCallCookie != nullptr)) - { - assert(!call->IsUnmanaged()); - - GenTree* arg = comp->gtNewIconEmbHndNode(call->gtCallCookie, GTF_ICON_PINVKI_HDL, nullptr); - call->gtCallCookie = nullptr; - - // TODO: this is preserving existing behavior, but do we actually need these NO_CSEs? - GenTree* argConst = arg->OperIs(GT_IND) ? arg->AsIndir()->Addr() : arg; - argConst->gtFlags |= GTF_DONT_CSE; - arg->gtFlags |= GTF_DONT_CSE; - - // All architectures pass the cookie in a register. - InsertAfterThisOrFirst(comp, NewCallArg::Primitive(arg).WellKnown(WellKnownArg::PInvokeCookie)); - // put destination into R10/EAX - arg = comp->gtClone(call->gtControlExpr, true); - // On x64 the pinvoke target is passed in r10 which is the same - // register as the gs cookie check may use. That would be a problem if - // this was a tailcall, but we do not tailcall functions with - // non-standard added args except indirection cells currently. - assert(!call->IsFastTailCall()); - InsertAfterThisOrFirst(comp, NewCallArg::Primitive(arg).WellKnown(WellKnownArg::PInvokeTarget)); - - // finally change this call to a helper call - call->gtCallType = CT_HELPER; - call->gtControlExpr = nullptr; - call->gtCallMethHnd = comp->eeFindHelper(CORINFO_HELP_PINVOKE_CALLI); - } #if defined(FEATURE_READYTORUN) #ifdef TARGET_WASM @@ -5824,7 +5796,6 @@ void Compiler::fgMorphTailCallViaJitHelper(GenTreeCall* call) // Check for PInvoke call types that we don't handle in codegen yet. assert(!call->IsUnmanaged()); - assert(call->IsVirtual() || (call->gtCallType != CT_INDIRECT) || (call->gtCallCookie == nullptr)); // Don't support tail calling helper methods assert(!call->IsHelperCall()); diff --git a/src/coreclr/jit/targetamd64.h b/src/coreclr/jit/targetamd64.h index f0ab6b24f44d78..56a4d5b0eeb876 100644 --- a/src/coreclr/jit/targetamd64.h +++ b/src/coreclr/jit/targetamd64.h @@ -384,14 +384,6 @@ // See ImportThunk.Kind.DelayLoadHelperWithExistingIndirectionCell in crossgen2. #define RBM_R2R_INDIRECT_PARAM RBM_RAX -// GenericPInvokeCalliHelper VASigCookie Parameter -#define REG_PINVOKE_COOKIE_PARAM REG_R11 -#define RBM_PINVOKE_COOKIE_PARAM RBM_R11 - -// GenericPInvokeCalliHelper unmanaged target Parameter -#define REG_PINVOKE_TARGET_PARAM REG_R10 -#define RBM_PINVOKE_TARGET_PARAM RBM_R10 - // IL stub's secret MethodDesc parameter (JitFlags::JIT_FLAG_PUBLISH_SECRET_PARAM) #define REG_SECRET_STUB_PARAM REG_R10 #define RBM_SECRET_STUB_PARAM RBM_R10 diff --git a/src/coreclr/jit/targetarm.h b/src/coreclr/jit/targetarm.h index 006bd4e0559b6b..9b431efcdaaa7c 100644 --- a/src/coreclr/jit/targetarm.h +++ b/src/coreclr/jit/targetarm.h @@ -162,14 +162,6 @@ // Registers no longer containing GC pointers after CORINFO_HELP_ASSIGN_REF and CORINFO_HELP_CHECKED_ASSIGN_REF. #define RBM_CALLEE_GCTRASH_WRITEBARRIER RBM_CALLEE_TRASH_WRITEBARRIER -// GenericPInvokeCalliHelper VASigCookie Parameter -#define REG_PINVOKE_COOKIE_PARAM REG_R4 -#define RBM_PINVOKE_COOKIE_PARAM RBM_R4 - -// GenericPInvokeCalliHelper unmanaged target Parameter -#define REG_PINVOKE_TARGET_PARAM REG_R12 -#define RBM_PINVOKE_TARGET_PARAM RBM_R12 - // IL stub's secret MethodDesc parameter (JitFlags::JIT_FLAG_PUBLISH_SECRET_PARAM) #define REG_SECRET_STUB_PARAM REG_R12 #define RBM_SECRET_STUB_PARAM RBM_R12 diff --git a/src/coreclr/jit/targetarm64.h b/src/coreclr/jit/targetarm64.h index 0f196cacf5e836..cc9c48583641c0 100644 --- a/src/coreclr/jit/targetarm64.h +++ b/src/coreclr/jit/targetarm64.h @@ -184,14 +184,6 @@ // Registers no longer containing GC pointers after CORINFO_HELP_ASSIGN_REF and CORINFO_HELP_CHECKED_ASSIGN_REF. #define RBM_CALLEE_GCTRASH_WRITEBARRIER RBM_CALLEE_TRASH_NOGC -// GenericPInvokeCalliHelper VASigCookie Parameter -#define REG_PINVOKE_COOKIE_PARAM REG_R15 -#define RBM_PINVOKE_COOKIE_PARAM RBM_R15 - -// GenericPInvokeCalliHelper unmanaged target Parameter -#define REG_PINVOKE_TARGET_PARAM REG_R12 -#define RBM_PINVOKE_TARGET_PARAM RBM_R12 - // IL stub's secret MethodDesc parameter (JitFlags::JIT_FLAG_PUBLISH_SECRET_PARAM) #define REG_SECRET_STUB_PARAM REG_R12 #define RBM_SECRET_STUB_PARAM RBM_R12 diff --git a/src/coreclr/jit/targetloongarch64.h b/src/coreclr/jit/targetloongarch64.h index 6acd2a67dfeebd..c4d9deae15789b 100644 --- a/src/coreclr/jit/targetloongarch64.h +++ b/src/coreclr/jit/targetloongarch64.h @@ -163,14 +163,6 @@ // Registers no longer containing GC pointers after CORINFO_HELP_ASSIGN_REF and CORINFO_HELP_CHECKED_ASSIGN_REF. #define RBM_CALLEE_GCTRASH_WRITEBARRIER RBM_CALLEE_TRASH_NOGC -// GenericPInvokeCalliHelper VASigCookie Parameter -#define REG_PINVOKE_COOKIE_PARAM REG_T3 -#define RBM_PINVOKE_COOKIE_PARAM RBM_T3 - -// GenericPInvokeCalliHelper unmanaged target Parameter -#define REG_PINVOKE_TARGET_PARAM REG_T2 -#define RBM_PINVOKE_TARGET_PARAM RBM_T2 - // IL stub's secret MethodDesc parameter (JitFlags::JIT_FLAG_PUBLISH_SECRET_PARAM) #define REG_SECRET_STUB_PARAM REG_T2 #define RBM_SECRET_STUB_PARAM RBM_T2 diff --git a/src/coreclr/jit/targetriscv64.h b/src/coreclr/jit/targetriscv64.h index 9679439d637025..3ec1ad135e5fd0 100644 --- a/src/coreclr/jit/targetriscv64.h +++ b/src/coreclr/jit/targetriscv64.h @@ -158,14 +158,6 @@ // Registers no longer containing GC pointers after CORINFO_HELP_ASSIGN_REF and CORINFO_HELP_CHECKED_ASSIGN_REF. #define RBM_CALLEE_GCTRASH_WRITEBARRIER RBM_CALLEE_TRASH_NOGC -// GenericPInvokeCalliHelper VASigCookie Parameter -#define REG_PINVOKE_COOKIE_PARAM REG_T3 -#define RBM_PINVOKE_COOKIE_PARAM RBM_T3 - -// GenericPInvokeCalliHelper unmanaged target Parameter -#define REG_PINVOKE_TARGET_PARAM REG_T2 -#define RBM_PINVOKE_TARGET_PARAM RBM_T2 - // IL stub's secret MethodDesc parameter (JitFlags::JIT_FLAG_PUBLISH_SECRET_PARAM) #define REG_SECRET_STUB_PARAM REG_T2 #define RBM_SECRET_STUB_PARAM RBM_T2 diff --git a/src/coreclr/jit/targetwasm.h b/src/coreclr/jit/targetwasm.h index 4c2ac831cb07d1..00ad95fe143f3b 100644 --- a/src/coreclr/jit/targetwasm.h +++ b/src/coreclr/jit/targetwasm.h @@ -155,14 +155,6 @@ // Registers no longer containing GC pointers after CORINFO_HELP_ASSIGN_REF and CORINFO_HELP_CHECKED_ASSIGN_REF. #define RBM_CALLEE_GCTRASH_WRITEBARRIER RBM_CALLEE_TRASH_NOGC -// GenericPInvokeCalliHelper VASigCookie Parameter -#define REG_PINVOKE_COOKIE_PARAM REG_NA -#define RBM_PINVOKE_COOKIE_PARAM RBM_NONE - -// GenericPInvokeCalliHelper unmanaged target Parameter -#define REG_PINVOKE_TARGET_PARAM REG_NA -#define RBM_PINVOKE_TARGET_PARAM RBM_NONE - // IL stub's secret MethodDesc parameter (JitFlags::JIT_FLAG_PUBLISH_SECRET_PARAM) #define REG_SECRET_STUB_PARAM REG_NA #define RBM_SECRET_STUB_PARAM RBM_NONE diff --git a/src/coreclr/jit/targetx86.h b/src/coreclr/jit/targetx86.h index dd338c316a3d0d..8d3d6c7eb5b8af 100644 --- a/src/coreclr/jit/targetx86.h +++ b/src/coreclr/jit/targetx86.h @@ -226,13 +226,6 @@ // Registers no longer containing GC pointers after CORINFO_HELP_ASSIGN_REF and CORINFO_HELP_CHECKED_ASSIGN_REF. #define RBM_CALLEE_GCTRASH_WRITEBARRIER RBM_EDX -// GenericPInvokeCalliHelper unmanaged target parameter -#define REG_PINVOKE_TARGET_PARAM REG_EAX -#define RBM_PINVOKE_TARGET_PARAM RBM_EAX - -// GenericPInvokeCalliHelper cookie parameter -#define REG_PINVOKE_COOKIE_PARAM REG_EBX - // IL stub's secret parameter (JitFlags::JIT_FLAG_PUBLISH_SECRET_PARAM) #define REG_SECRET_STUB_PARAM REG_EAX #define RBM_SECRET_STUB_PARAM RBM_EAX diff --git a/src/coreclr/jit/wellknownargs.h b/src/coreclr/jit/wellknownargs.h index 23976312e5e844..b08800173046c7 100644 --- a/src/coreclr/jit/wellknownargs.h +++ b/src/coreclr/jit/wellknownargs.h @@ -26,8 +26,6 @@ WELL_KNOWN_ARG(PInvokeFrame, "pinv frame", false, false) WELL_KNOWN_ARG(ShiftLow, "shift low", false, false) WELL_KNOWN_ARG(ShiftHigh, "shift high", false, false) WELL_KNOWN_ARG(VirtualStubCell, "vsd cell", false, true) -WELL_KNOWN_ARG(PInvokeCookie, "pinv cookie", false, true) -WELL_KNOWN_ARG(PInvokeTarget, "pinv tgt", false, true) WELL_KNOWN_ARG(R2RIndirectionCell, "r2r cell", false, true) WELL_KNOWN_ARG(ValidateIndirectCallTarget, "cfg tgt", false, false) WELL_KNOWN_ARG(DispatchIndirectCallTarget, "cfg tgt", false, false) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs b/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs index 68f7b52f6ec048..ab7ca8480c7a54 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs @@ -179,7 +179,6 @@ which is the right helper to use to allocate an object of a given type. */ /* Miscellaneous */ - CORINFO_HELP_PINVOKE_CALLI, // Indirect pinvoke call CORINFO_HELP_TAILCALL, // Perform a tail call CORINFO_HELP_GETCURRENTMANAGEDTHREADID, diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 51be0ec02ec06a..d001e253a7d5c3 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -4151,14 +4151,6 @@ private void getLocationOfThisType(CORINFO_METHOD_STRUCT_* context, ref CORINFO_ private void* GetCookieForInterpreterCalliSig(CORINFO_SIG_INFO* szMetaSig) { throw new NotImplementedException("GetCookieForInterpreterCalliSig"); } - private void* GetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig, ref void* ppIndirection) - { -#if READYTORUN - throw new RequiresRuntimeJitException($"{MethodBeingCompiled} -> {nameof(GetCookieForPInvokeCalliSig)}"); -#else - throw new NotImplementedException(nameof(GetCookieForPInvokeCalliSig)); -#endif - } #pragma warning disable CA1822 // Mark members as static private CORINFO_JUST_MY_CODE_HANDLE_* getJustMyCodeHandle(CORINFO_METHOD_STRUCT_* method, ref CORINFO_JUST_MY_CODE_HANDLE_* ppIndirection) #pragma warning restore CA1822 // Mark members as static diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs index 3cad520435442b..43703ca20dfe6a 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs @@ -165,7 +165,6 @@ static ICorJitInfoCallbacks() s_callbacks.embedGenericHandle = &_embedGenericHandle; s_callbacks.getLocationOfThisType = &_getLocationOfThisType; s_callbacks.getAddressOfPInvokeTarget = &_getAddressOfPInvokeTarget; - s_callbacks.GetCookieForPInvokeCalliSig = &_GetCookieForPInvokeCalliSig; s_callbacks.GetCookieForInterpreterCalliSig = &_GetCookieForInterpreterCalliSig; s_callbacks.getJustMyCodeHandle = &_getJustMyCodeHandle; s_callbacks.GetProfilingHandle = &_GetProfilingHandle; @@ -351,7 +350,6 @@ static ICorJitInfoCallbacks() public delegate* unmanaged embedGenericHandle; public delegate* unmanaged getLocationOfThisType; public delegate* unmanaged getAddressOfPInvokeTarget; - public delegate* unmanaged GetCookieForPInvokeCalliSig; public delegate* unmanaged GetCookieForInterpreterCalliSig; public delegate* unmanaged getJustMyCodeHandle; public delegate* unmanaged GetProfilingHandle; @@ -2533,21 +2531,6 @@ private static void _getAddressOfPInvokeTarget(IntPtr thisHandle, IntPtr* ppExce } } - [UnmanagedCallersOnly] - private static void* _GetCookieForPInvokeCalliSig(IntPtr thisHandle, IntPtr* ppException, CORINFO_SIG_INFO* szMetaSig, void** ppIndirection) - { - var _this = GetThis(thisHandle); - try - { - return _this.GetCookieForPInvokeCalliSig(szMetaSig, ref *ppIndirection); - } - catch (Exception ex) - { - *ppException = _this.AllocException(ex); - return default; - } - } - [UnmanagedCallersOnly] private static void* _GetCookieForInterpreterCalliSig(IntPtr thisHandle, IntPtr* ppException, CORINFO_SIG_INFO* szMetaSig) { diff --git a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt index 2408217150bfb6..668faa8e994b87 100644 --- a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt +++ b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt @@ -320,7 +320,6 @@ FUNCTIONS void embedGenericHandle(CORINFO_RESOLVED_TOKEN * pResolvedToken, bool fEmbedParent, CORINFO_METHOD_HANDLE callerHandle, CORINFO_GENERICHANDLE_RESULT * pResult); void getLocationOfThisType(CORINFO_METHOD_HANDLE context, CORINFO_LOOKUP_KIND* pLookupKind); void getAddressOfPInvokeTarget(CORINFO_METHOD_HANDLE method, REF_CORINFO_CONST_LOOKUP pLookup); - void* GetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig, void ** ppIndirection); void* GetCookieForInterpreterCalliSig(CORINFO_SIG_INFO* szMetaSig); CORINFO_JUST_MY_CODE_HANDLE getJustMyCodeHandle(CORINFO_METHOD_HANDLE method, CORINFO_JUST_MY_CODE_HANDLE**ppIndirection); void GetProfilingHandle(bool* pbHookFunction, void **pProfilerHandle, bool* pbIndirectedHandles); diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs index 565034235355b5..887a3774d2584d 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -3332,7 +3332,33 @@ private bool pInvokeMarshalingRequired(CORINFO_METHOD_STRUCT_* handle, CORINFO_S private bool convertPInvokeCalliToCall(ref CORINFO_RESOLVED_TOKEN pResolvedToken, bool mustConvert) { - throw new NotImplementedException(); + var methodIL = (MethodIL)HandleToObject((void*)pResolvedToken.tokenScope); + var signature = (MethodSignature)methodIL.GetObject((int)pResolvedToken.token); + + switch (signature.Flags & MethodSignatureFlags.UnmanagedCallingConventionMask) + { + case MethodSignatureFlags.None: + case MethodSignatureFlags.CallingConventionVarargs: + // Not an unmanaged call site. + return false; + } + + CorInfoCallConvExtension unmanagedCallConv = GetUnmanagedCallConv(signature, out _); + + // ReadyToRun has no way to refer to the marshalling stub the runtime creates for an unmanaged + // calli, so the JIT has to expand the call site inline. Everything it cannot expand - a call site + // where an inline P/Invoke frame is not allowed, a calling convention the JIT refuses to inline, + // an unmanaged signature carrying an instance 'this', or a signature that needs marshalling - + // leaves the method to the runtime JIT. + if (mustConvert || + !signature.IsStatic || signature.IsExplicitThis || + unmanagedCallConv is CorInfoCallConvExtension.Fastcall or CorInfoCallConvExtension.FastcallMemberFunction || + Marshaller.IsMarshallingRequired(signature, Array.Empty(), ((MetadataType)methodIL.OwningMethod.OwningType).Module)) + { + throw new RequiresRuntimeJitException($"{MethodBeingCompiled} -> {nameof(convertPInvokeCalliToCall)}"); + } + + return false; } private int SizeOfPInvokeTransitionFrame => ReadyToRunRuntimeConstants.READYTORUN_PInvokeTransitionFrameSizeInPointerUnits * PointerSize; diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs index 14f0f9b6c1e33a..0d486b639a870e 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs @@ -2021,7 +2021,6 @@ private bool convertPInvokeCalliToCall(ref CORINFO_RESOLVED_TOKEN pResolvedToken return false; pResolvedToken.hMethod = ObjectToHandle(stub); - pResolvedToken.hClass = ObjectToHandle(stub.OwningType); return true; } diff --git a/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h b/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h index 63c3f1ea266574..e81f85ac47fa20 100644 --- a/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h +++ b/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h @@ -156,7 +156,6 @@ struct JitInterfaceCallbacks void (* embedGenericHandle)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_RESOLVED_TOKEN* pResolvedToken, bool fEmbedParent, CORINFO_METHOD_HANDLE callerHandle, CORINFO_GENERICHANDLE_RESULT* pResult); void (* getLocationOfThisType)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_METHOD_HANDLE context, CORINFO_LOOKUP_KIND* pLookupKind); void (* getAddressOfPInvokeTarget)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_METHOD_HANDLE method, CORINFO_CONST_LOOKUP* pLookup); - void* (* GetCookieForPInvokeCalliSig)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_SIG_INFO* szMetaSig, void** ppIndirection); void* (* GetCookieForInterpreterCalliSig)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_SIG_INFO* szMetaSig); CORINFO_JUST_MY_CODE_HANDLE (* getJustMyCodeHandle)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_METHOD_HANDLE method, CORINFO_JUST_MY_CODE_HANDLE** ppIndirection); void (* GetProfilingHandle)(void * thisHandle, CorInfoExceptionClass** ppException, bool* pbHookFunction, void** pProfilerHandle, bool* pbIndirectedHandles); @@ -1612,16 +1611,6 @@ class JitInterfaceWrapper : public ICorJitInfo if (pException != nullptr) throw pException; } - virtual void* GetCookieForPInvokeCalliSig( - CORINFO_SIG_INFO* szMetaSig, - void** ppIndirection) -{ - CorInfoExceptionClass* pException = nullptr; - void* temp = _callbacks->GetCookieForPInvokeCalliSig(_thisHandle, &pException, szMetaSig, ppIndirection); - if (pException != nullptr) throw pException; - return temp; -} - virtual void* GetCookieForInterpreterCalliSig( CORINFO_SIG_INFO* szMetaSig) { diff --git a/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h b/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h index eaf9e7c905322e..8ab7e6026528f1 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h @@ -735,14 +735,6 @@ struct GetVarArgsHandleValue DWORDLONG methHnd; }; -struct GetCookieForPInvokeCalliSigValue -{ - DWORD cbSig; - DWORD pSig_Index; - DWORDLONG scope; - DWORD token; -}; - struct GetCookieForInterpreterCalliSigValue { DWORD cbSig; diff --git a/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h b/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h index bc1bedc95085a8..86d13380397669 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h @@ -71,7 +71,6 @@ LWM(GetClassNumInstanceFields, DWORDLONG, DWORD) LWM(GetClassSize, DWORDLONG, DWORD) LWM(GetHeapClassSize, DWORDLONG, DWORD) LWM(CanAllocateOnStack, DWORDLONG, DWORD) -LWM(GetCookieForPInvokeCalliSig, GetCookieForPInvokeCalliSigValue, DLDL) LWM(GetCookieForInterpreterCalliSig, GetCookieForInterpreterCalliSigValue, DLDL) LWM(GetDefaultComparerClass, DWORDLONG, DWORDLONG) LWM(GetDefaultEqualityComparerClass, DWORDLONG, DWORDLONG) diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp index 560bfb72019717..cc0970f66123ab 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp @@ -6115,52 +6115,6 @@ TypeCompareState MethodContext::repIsEnum(CORINFO_CLASS_HANDLE cls, CORINFO_CLAS return (TypeCompareState)value.B; } -void MethodContext::recGetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig, void** ppIndirection, LPVOID result) -{ - if (GetCookieForPInvokeCalliSig == nullptr) - GetCookieForPInvokeCalliSig = new LightWeightMap(); - - GetCookieForPInvokeCalliSigValue key; - ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding - key.cbSig = (DWORD)szMetaSig->cbSig; - key.pSig_Index = (DWORD)GetCookieForPInvokeCalliSig->AddBuffer((unsigned char*)szMetaSig->pSig, szMetaSig->cbSig); - key.scope = CastHandle(szMetaSig->scope); - key.token = (DWORD)szMetaSig->token; - - DLDL value; - if (ppIndirection != nullptr) - value.A = CastPointer(*ppIndirection); - else - value.A = 0; - value.B = CastPointer(result); - - GetCookieForPInvokeCalliSig->Add(key, value); - DEBUG_REC(dmpGetCookieForPInvokeCalliSig(key, value)); -} -void MethodContext::dmpGetCookieForPInvokeCalliSig(const GetCookieForPInvokeCalliSigValue& key, DLDL value) -{ - printf("GetCookieForPInvokeCalliSig NYI"); -} -LPVOID MethodContext::repGetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig, void** ppIndirection) -{ - AssertMapExistsNoMessage(GetCookieForPInvokeCalliSig); - - GetCookieForPInvokeCalliSigValue key; - ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding - key.cbSig = (DWORD)szMetaSig->cbSig; - key.pSig_Index = (DWORD)GetCookieForPInvokeCalliSig->Contains((unsigned char*)szMetaSig->pSig, szMetaSig->cbSig); - key.scope = CastHandle(szMetaSig->scope); - key.token = (DWORD)szMetaSig->token; - - DLDL value = LookupByKeyOrMissNoMessage(GetCookieForPInvokeCalliSig, key); - - DEBUG_REP(dmpGetCookieForPInvokeCalliSig(key, value)); - - if (ppIndirection != nullptr) - *ppIndirection = (void*)value.A; - return (CORINFO_VARARGS_HANDLE)value.B; -} - void MethodContext::recGetCookieForInterpreterCalliSig(CORINFO_SIG_INFO* szMetaSig, LPVOID result) { if (GetCookieForInterpreterCalliSig == nullptr) diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h index 3a4664fab8af6e..c0a4c49d4141f2 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h @@ -757,10 +757,6 @@ class MethodContext void dmpIsEnum(DWORDLONG key, DLD value); TypeCompareState repIsEnum(CORINFO_CLASS_HANDLE cls, CORINFO_CLASS_HANDLE* underlyingType); - void recGetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig, void** ppIndirection, LPVOID result); - void dmpGetCookieForPInvokeCalliSig(const GetCookieForPInvokeCalliSigValue& key, DLDL value); - LPVOID repGetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig, void** ppIndirection); - LPVOID repGetCookieForInterpreterCalliSig(CORINFO_SIG_INFO* szMetaSig); void recGetCookieForInterpreterCalliSig(CORINFO_SIG_INFO* szMetaSig, LPVOID result); void dmpGetCookieForInterpreterCalliSig(const GetCookieForInterpreterCalliSigValue& key, DLDL value); diff --git a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp index 82babd5a9d9964..1d71c7713c27a9 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp @@ -1635,15 +1635,6 @@ void interceptor_ICJI::getAddressOfPInvokeTarget(CORINFO_METHOD_HANDLE method, C mc->recGetAddressOfPInvokeTarget(method, pLookup); } -// Generate a cookie based on the signature to pass to CORINFO_HELP_PINVOKE_CALLI -LPVOID interceptor_ICJI::GetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig, void** ppIndirection) -{ - mc->cr->AddCall("GetCookieForPInvokeCalliSig"); - LPVOID temp = original_ICorJitInfo->GetCookieForPInvokeCalliSig(szMetaSig, ppIndirection); - mc->recGetCookieForPInvokeCalliSig(szMetaSig, ppIndirection, temp); - return temp; -} - // Generate a cookie based on the signature to pass to INTOP_CALLI LPVOID interceptor_ICJI::GetCookieForInterpreterCalliSig(CORINFO_SIG_INFO* szMetaSig) { diff --git a/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp b/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp index de53f39764e9db..51f6d501025f28 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp @@ -1175,14 +1175,6 @@ void interceptor_ICJI::getAddressOfPInvokeTarget( original_ICorJitInfo->getAddressOfPInvokeTarget(method, pLookup); } -void* interceptor_ICJI::GetCookieForPInvokeCalliSig( - CORINFO_SIG_INFO* szMetaSig, - void** ppIndirection) -{ - mcs->AddCall("GetCookieForPInvokeCalliSig"); - return original_ICorJitInfo->GetCookieForPInvokeCalliSig(szMetaSig, ppIndirection); -} - void* interceptor_ICJI::GetCookieForInterpreterCalliSig( CORINFO_SIG_INFO* szMetaSig) { diff --git a/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp b/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp index e51bb1ecbc4f1f..b04af78797d442 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp @@ -1030,13 +1030,6 @@ void interceptor_ICJI::getAddressOfPInvokeTarget( original_ICorJitInfo->getAddressOfPInvokeTarget(method, pLookup); } -void* interceptor_ICJI::GetCookieForPInvokeCalliSig( - CORINFO_SIG_INFO* szMetaSig, - void** ppIndirection) -{ - return original_ICorJitInfo->GetCookieForPInvokeCalliSig(szMetaSig, ppIndirection); -} - void* interceptor_ICJI::GetCookieForInterpreterCalliSig( CORINFO_SIG_INFO* szMetaSig) { diff --git a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp index 4b8c67aef84bb3..c23a2247efbea6 100644 --- a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp @@ -1401,13 +1401,6 @@ void MyICJI::getAddressOfPInvokeTarget(CORINFO_METHOD_HANDLE method, CORINFO_CON jitInstance->mc->repGetAddressOfPInvokeTarget(method, pLookup); } -// Generate a cookie based on the signature to pass to CORINFO_HELP_PINVOKE_CALLI -LPVOID MyICJI::GetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig, void** ppIndirection) -{ - jitInstance->mc->cr->AddCall("GetCookieForPInvokeCalliSig"); - return jitInstance->mc->repGetCookieForPInvokeCalliSig(szMetaSig, ppIndirection); -} - // Generate a cookie based on the signature to pass to INTOP_CALLI LPVOID MyICJI::GetCookieForInterpreterCalliSig(CORINFO_SIG_INFO* szMetaSig) { diff --git a/src/coreclr/vm/FrameTypes.h b/src/coreclr/vm/FrameTypes.h index 03ea0dd0f4cdb6..42601a1b8bcfea 100644 --- a/src/coreclr/vm/FrameTypes.h +++ b/src/coreclr/vm/FrameTypes.h @@ -20,7 +20,6 @@ FRAME_TYPE_NAME(FuncEvalFrame) #endif // DEBUGGING_SUPPORTED #ifdef FEATURE_COMINTEROP #endif // FEATURE_COMINTEROP -FRAME_TYPE_NAME(PInvokeCalliFrame) #ifdef FEATURE_HIJACK FRAME_TYPE_NAME(HijackFrame) #endif // FEATURE_HIJACK diff --git a/src/coreclr/vm/amd64/PInvokeStubs.asm b/src/coreclr/vm/amd64/PInvokeStubs.asm index 2238239e558b88..fae9b6ac676b6b 100644 --- a/src/coreclr/vm/amd64/PInvokeStubs.asm +++ b/src/coreclr/vm/amd64/PInvokeStubs.asm @@ -4,65 +4,11 @@ include AsmMacros.inc include AsmConstants.inc -extern GenericPInvokeCalliStubWorker:proc extern VarargPInvokeStubWorker:proc extern JIT_PInvokeEndRarePath:proc extern g_TrapReturningThreads:DWORD -; -; in: -; PINVOKE_CALLI_TARGET_REGISTER (r10) = unmanaged target -; PINVOKE_CALLI_SIGTOKEN_REGNUM (r11) = sig token -; -; out: -; METHODDESC_REGISTER (r10) = unmanaged target -; -LEAF_ENTRY GenericPInvokeCalliHelper, _TEXT - - ; - ; check for existing IL stub - ; - mov rax, [PINVOKE_CALLI_SIGTOKEN_REGISTER + OFFSETOF__VASigCookie__pPInvokeILStub] - test rax, rax - jz GenericPInvokeCalliGenILStub - - ; - ; jump to existing IL stub - ; - jmp rax - -LEAF_END GenericPInvokeCalliHelper, _TEXT - -NESTED_ENTRY GenericPInvokeCalliGenILStub, _TEXT - - PROLOG_WITH_TRANSITION_BLOCK - - ; - ; save target - ; - mov r12, METHODDESC_REGISTER - mov r13, PINVOKE_CALLI_SIGTOKEN_REGISTER - - ; - ; GenericPInvokeCalliStubWorker(TransitionBlock * pTransitionBlock, VASigCookie * pVASigCookie, PCODE pUnmanagedTarget) - ; - lea rcx, [rsp + __PWTB_TransitionBlock] ; pTransitionBlock* - mov rdx, PINVOKE_CALLI_SIGTOKEN_REGISTER ; pVASigCookie - mov r8, METHODDESC_REGISTER ; pUnmanagedTarget - call GenericPInvokeCalliStubWorker - - ; - ; restore target - ; - mov METHODDESC_REGISTER, r12 - mov PINVOKE_CALLI_SIGTOKEN_REGISTER, r13 - - EPILOG_WITH_TRANSITION_BLOCK_TAILCALL - jmp GenericPInvokeCalliHelper - -NESTED_END GenericPInvokeCalliGenILStub, _TEXT - LEAF_ENTRY VarargPInvokeStub, _TEXT mov PINVOKE_CALLI_SIGTOKEN_REGISTER, rcx jmp VarargPInvokeStubHelper diff --git a/src/coreclr/vm/amd64/asmconstants.h b/src/coreclr/vm/amd64/asmconstants.h index f6507886d6bda0..4afd23572e6d53 100644 --- a/src/coreclr/vm/amd64/asmconstants.h +++ b/src/coreclr/vm/amd64/asmconstants.h @@ -53,9 +53,6 @@ ASMCONSTANTS_C_ASSERT(ASM_ELEMENT_TYPE_R8 == ELEMENT_TYPE_R8); #define METHODDESC_REGNUM 10 #define METHODDESC_REGISTER r10 -#define PINVOKE_CALLI_TARGET_REGNUM 10 -#define PINVOKE_CALLI_TARGET_REGISTER r10 - #define PINVOKE_CALLI_SIGTOKEN_REGNUM 11 #define PINVOKE_CALLI_SIGTOKEN_REGISTER r11 diff --git a/src/coreclr/vm/amd64/pinvokestubs.S b/src/coreclr/vm/amd64/pinvokestubs.S index 58d96db85f0d4d..ae1e1b166f5fed 100644 --- a/src/coreclr/vm/amd64/pinvokestubs.S +++ b/src/coreclr/vm/amd64/pinvokestubs.S @@ -6,59 +6,6 @@ #include "asmconstants.h" -// -// in: -// PINVOKE_CALLI_TARGET_REGISTER (r10) = unmanaged target -// PINVOKE_CALLI_SIGTOKEN_REGNUM (r11) = sig token -// -// out: -// METHODDESC_REGISTER (r10) = unmanaged target -// -LEAF_ENTRY GenericPInvokeCalliHelper, _TEXT - - // - // check for existing IL stub - // - mov rax, [PINVOKE_CALLI_SIGTOKEN_REGISTER + OFFSETOF__VASigCookie__pPInvokeILStub] - test rax, rax - jz C_FUNC(GenericPInvokeCalliGenILStub) - - // - // jump to existing IL stub - // - jmp rax - -LEAF_END GenericPInvokeCalliHelper, _TEXT - -NESTED_ENTRY GenericPInvokeCalliGenILStub, _TEXT, NoHandler - - PROLOG_WITH_TRANSITION_BLOCK - - // - // save target - // - mov r12, METHODDESC_REGISTER - mov r13, PINVOKE_CALLI_SIGTOKEN_REGISTER - - // - // GenericPInvokeCalliStubWorker(TransitionBlock * pTransitionBlock, VASigCookie * pVASigCookie, PCODE pUnmanagedTarget) - // - lea rdi, [rsp + __PWTB_TransitionBlock] // pTransitionBlock* - mov rsi, PINVOKE_CALLI_SIGTOKEN_REGISTER // pVASigCookie - mov rdx, METHODDESC_REGISTER // pUnmanagedTarget - call C_FUNC(GenericPInvokeCalliStubWorker) - - // - // restore target - // - mov METHODDESC_REGISTER, r12 - mov PINVOKE_CALLI_SIGTOKEN_REGISTER, r13 - - EPILOG_WITH_TRANSITION_BLOCK_TAILCALL - jmp C_FUNC(GenericPInvokeCalliHelper) - -NESTED_END GenericPInvokeCalliGenILStub, _TEXT - LEAF_ENTRY VarargPInvokeStub, _TEXT mov PINVOKE_CALLI_SIGTOKEN_REGISTER, rdi jmp C_FUNC(VarargPInvokeStubHelper) diff --git a/src/coreclr/vm/arm/pinvokestubs.S b/src/coreclr/vm/arm/pinvokestubs.S index b102ae12d793e4..f0d22819a5fb9f 100644 --- a/src/coreclr/vm/arm/pinvokestubs.S +++ b/src/coreclr/vm/arm/pinvokestubs.S @@ -200,16 +200,6 @@ LOCAL_LABEL(RarePath): // PINVOKE_STUB VarargPInvokeStub, VarargPInvokeGenILStub, VarargPInvokeStubWorker, r0, 0 -// ------------------------------------------------------------------ -// GenericPInvokeCalliHelper & GenericPInvokeCalliGenILStub -// Helper for generic pinvoke calli instruction -// -// in: -// r4 = VASigCookie* -// r12 = Unmanaged target -// -PINVOKE_STUB GenericPInvokeCalliHelper, GenericPInvokeCalliGenILStub, GenericPInvokeCalliStubWorker r4, 1 - // ------------------------------------------------------------------ // VarargPInvokeStub_RetBuffArg & VarargPInvokeGenILStub_RetBuffArg // Vararg PInvoke Stub when the method has a hidden return buffer arg diff --git a/src/coreclr/vm/arm64/PInvokeStubs.asm b/src/coreclr/vm/arm64/PInvokeStubs.asm index e02cea49ac435c..dcbacd39d97722 100644 --- a/src/coreclr/vm/arm64/PInvokeStubs.asm +++ b/src/coreclr/vm/arm64/PInvokeStubs.asm @@ -9,7 +9,6 @@ IMPORT VarargPInvokeStubWorker - IMPORT GenericPInvokeCalliStubWorker IMPORT JIT_PInvokeEndRarePath IMPORT g_TrapReturningThreads @@ -21,7 +20,7 @@ ; ; Params :- ; $FuncPrefix : prefix of the function name for the stub -; Eg. VarargPinvoke, GenericPInvokeCalli +; Eg. VarargPinvoke ; $VASigCookieReg : register which contains the VASigCookie ; $SaveFPArgs : "Yes" or "No" . For varidic functions FP Args are not present in FP regs ; So need not save FP Args registers for vararg Pinvoke @@ -33,11 +32,7 @@ GBLS __PInvokeGenStubFuncName GBLS __PInvokeStubWorkerName - IF "$FuncPrefix" == "GenericPInvokeCalli" -__PInvokeStubFuncName SETS "$FuncPrefix":CC:"Helper" - ELSE __PInvokeStubFuncName SETS "$FuncPrefix":CC:"Stub" - ENDIF __PInvokeGenStubFuncName SETS "$FuncPrefix":CC:"GenILStub" __PInvokeStubWorkerName SETS "$FuncPrefix":CC:"StubWorker" @@ -176,16 +171,5 @@ RarePath PINVOKE_STUB VarargPInvoke, x0, x12, {false} -; ------------------------------------------------------------------ -; GenericPInvokeCalliHelper & GenericPInvokeCalliGenILStub -; Helper for generic pinvoke calli instruction -; -; in: -; x15 = VASigCookie* -; x12 = Unmanaged target -; - PINVOKE_STUB GenericPInvokeCalli, x15, x12, {true} - - ; Must be at very end of file END diff --git a/src/coreclr/vm/arm64/pinvokestubs.S b/src/coreclr/vm/arm64/pinvokestubs.S index 61b96e94f50b80..a6dce447ef5219 100644 --- a/src/coreclr/vm/arm64/pinvokestubs.S +++ b/src/coreclr/vm/arm64/pinvokestubs.S @@ -11,7 +11,7 @@ // // Params :- // $FuncPrefix : prefix of the function name for the stub -// Eg. VarargPinvoke, GenericPInvokeCalli +// Eg. VarargPinvoke // $VASigCookieReg : register which contains the VASigCookie // $SaveFPArgs : "Yes" or "No" . For varidic functions FP Args are not present in FP regs // So need not save FP Args registers for vararg Pinvoke @@ -157,14 +157,3 @@ LOCAL_LABEL(RarePath): // x12 = MethodDesc * // PINVOKE_STUB VarargPInvokeStub, VarargPInvokeGenILStub, VarargPInvokeStubWorker, x0, x12, 0 - - -// ------------------------------------------------------------------ -// GenericPInvokeCalliHelper & GenericPInvokeCalliGenILStub -// Helper for generic pinvoke calli instruction -// -// in: -// x15 = VASigCookie* -// x12 = Unmanaged target -// -PINVOKE_STUB GenericPInvokeCalliHelper, GenericPInvokeCalliGenILStub, GenericPInvokeCalliStubWorker, x15, x12, 1 diff --git a/src/coreclr/vm/callconvbuilder.cpp b/src/coreclr/vm/callconvbuilder.cpp index a42b3b908a07f7..c3ad9a2800b61e 100644 --- a/src/coreclr/vm/callconvbuilder.cpp +++ b/src/coreclr/vm/callconvbuilder.cpp @@ -393,7 +393,6 @@ HRESULT CallConv::TryGetUnmanagedCallingConventionFromModOptSigStartingAtRetType LPCSTR typeName; if (*pWalk == ELEMENT_TYPE_CMOD_INTERNAL) { - // Skip internal modifiers pWalk++; if (pWalk + 1 + sizeof(void*) > pSig + cSig) { @@ -405,10 +404,14 @@ HRESULT CallConv::TryGetUnmanagedCallingConventionFromModOptSigStartingAtRetType void* pType; pWalk += CorSigUncompressPointer(pWalk, &pType); TypeHandle type = TypeHandle::FromPtr(pType); - - if (!required) + + // Calling conventions are only ever expressed as optional modifiers. A module + // independent signature encodes ELEMENT_TYPE_CMOD_OPT as an internal modifier with + // the "is required" byte cleared (see code:SigPointer::ConvertToInternalExactlyOne), + // so skip the required ones just like the token based case below does. + if (required) continue; - + tokenLookupModule = GetScopeHandle(type.GetModule()); tk = type.GetCl(); } @@ -430,7 +433,7 @@ HRESULT CallConv::TryGetUnmanagedCallingConventionFromModOptSigStartingAtRetType } // Check for CallConv types specified in modopt - if (FAILED(GetNameOfTypeRefOrDef(pModule, tk, &typeNamespace, &typeName))) + if (FAILED(GetNameOfTypeRefOrDef(tokenLookupModule, tk, &typeNamespace, &typeName))) continue; if (::strcmp(typeNamespace, CMOD_CALLCONV_NAMESPACE) != 0) diff --git a/src/coreclr/vm/ceeload.cpp b/src/coreclr/vm/ceeload.cpp index c8e7e987be3f7a..c1c6346c34acb1 100644 --- a/src/coreclr/vm/ceeload.cpp +++ b/src/coreclr/vm/ceeload.cpp @@ -4231,46 +4231,59 @@ static bool MethodSignatureContainsGenericVariables(SigParser& sp) } //========================================================================== -// Enregisters a VASig. +// Computes the module that should own runtime artifacts (VASigCookies, CALLI IL stubs) +// created for the given standalone signature. +// +// The generic context is stripped if it is not actually used by the signature. This is +// necessary for both: +// - Performance: allows more sharing of the created artifacts +// - Functionality: built-in runtime marshalling is disallowed for generic signatures //========================================================================== -VASigCookie *Module::GetVASigCookie(Signature vaSignature, const SigTypeContext* typeContext) +Module* Module::GetLoaderModuleForSignature(Signature signature, SigTypeContext* pTypeContext) { CONTRACTL { INSTANCE_CHECK; STANDARD_VM_CHECK; - INJECT_FAULT(COMPlusThrowOM()); + PRECONDITION(CheckPointer(pTypeContext)); } CONTRACTL_END; - SigTypeContext emptyContext; + SigParser sigParser = signature.CreateSigParser(); - Module* pLoaderModule = this; - if (!typeContext->IsEmpty()) - { - // Strip the generic context if it is not actually used by the signature. It is nececessary for both: - // - Performance: allow more sharing of vasig cookies - // - Functionality: built-in runtime marshalling is disallowed for generic signatures - SigParser sigParser = vaSignature.CreateSigParser(); - if (MethodSignatureContainsGenericVariables(sigParser)) - { - pLoaderModule = ClassLoader::ComputeLoaderModuleWorker(this, mdTokenNil, typeContext->m_classInst, typeContext->m_methodInst); - } - else - { - typeContext = &emptyContext; - } - } - else + if (pTypeContext->IsEmpty()) { -#ifdef _DEBUG // The method signature should not contain any generic variables if the generic context is not provided. - SigParser sigParser = vaSignature.CreateSigParser(); _ASSERTE(!MethodSignatureContainsGenericVariables(sigParser)); -#endif + return this; } - VASigCookie *pCookie = GetVASigCookieWorker(this, pLoaderModule, vaSignature, typeContext); + if (!MethodSignatureContainsGenericVariables(sigParser)) + { + *pTypeContext = SigTypeContext(); + return this; + } + + return ClassLoader::ComputeLoaderModuleWorker(this, mdTokenNil, pTypeContext->m_classInst, pTypeContext->m_methodInst); +} + +//========================================================================== +// Enregisters a VASig. +//========================================================================== +VASigCookie *Module::GetVASigCookie(Signature vaSignature, const SigTypeContext* typeContext) +{ + CONTRACTL + { + INSTANCE_CHECK; + STANDARD_VM_CHECK; + INJECT_FAULT(COMPlusThrowOM()); + } + CONTRACTL_END; + + SigTypeContext localContext = *typeContext; + Module* pLoaderModule = GetLoaderModuleForSignature(vaSignature, &localContext); + + VASigCookie *pCookie = GetVASigCookieWorker(this, pLoaderModule, vaSignature, &localContext); return pCookie; } diff --git a/src/coreclr/vm/ceeload.h b/src/coreclr/vm/ceeload.h index 8f0d190464fa87..16a6d1afb66dd1 100644 --- a/src/coreclr/vm/ceeload.h +++ b/src/coreclr/vm/ceeload.h @@ -1418,6 +1418,10 @@ class Module : public ModuleBase // Enregisters a VASig. VASigCookie *GetVASigCookie(Signature vaSignature, const SigTypeContext* typeContext); + + // Computes the module that owns runtime artifacts created for a standalone signature. + // Clears *pTypeContext if the signature does not actually use the generic context. + Module* GetLoaderModuleForSignature(Signature signature, SigTypeContext* pTypeContext); private: static VASigCookie *GetVASigCookieWorker(Module* pDefiningModule, Module* pLoaderModule, Signature vaSignature, const SigTypeContext* typeContext); diff --git a/src/coreclr/vm/cgensys.h b/src/coreclr/vm/cgensys.h index 9f0a24383f8ff5..49f5da8e49a914 100644 --- a/src/coreclr/vm/cgensys.h +++ b/src/coreclr/vm/cgensys.h @@ -50,8 +50,6 @@ extern "C" void STDCALL VarargPInvokeStubWorker(TransitionBlock* pTransitionBloc extern "C" void STDCALL VarargPInvokeStub(void); extern "C" void STDCALL VarargPInvokeStub_RetBuffArg(void); -extern "C" void STDCALL GenericPInvokeCalliStubWorker(TransitionBlock * pTransitionBlock, VASigCookie * pVASigCookie, PCODE pUnmanagedTarget); -extern "C" void STDCALL GenericPInvokeCalliHelper(void); extern "C" PCODE STDCALL ExternalMethodFixupWorker(TransitionBlock * pTransitionBlock, TADDR pIndirection, DWORD sectionIndex, Module * pModule); diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index c955c16b2a52da..9cd459cd52ebd5 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -1067,6 +1067,7 @@ DEFINE_METHOD(STUBHELPERS, SET_LAST_ERROR, SetLastError, DEFINE_METHOD(STUBHELPERS, CLEAR_LAST_ERROR, ClearLastError, SM_RetVoid) DEFINE_METHOD(STUBHELPERS, THROW_INTEROP_PARAM_EXCEPTION, ThrowInteropParamException, SM_Int_Int_RetVoid) +DEFINE_METHOD(STUBHELPERS, THROW_INTEROP_EXCEPTION, ThrowInteropException, SM_Int_Int_RetVoid) DEFINE_METHOD(STUBHELPERS, ADD_TO_CLEANUP_LIST_SAFEHANDLE, AddToCleanupList, SM_RefCleanupWorkListElement_SafeHandle_RetIntPtr) DEFINE_METHOD(STUBHELPERS, DESTROY_CLEANUP_LIST, DestroyCleanupList, SM_RefCleanupWorkListElement_RetVoid) DEFINE_METHOD(STUBHELPERS, GET_HR_EXCEPTION_OBJECT, GetHRExceptionObject, SM_Int_RetException) diff --git a/src/coreclr/vm/datadescriptor/datadescriptor.inc b/src/coreclr/vm/datadescriptor/datadescriptor.inc index a8163e3d1dfb41..d60f7f99a749ff 100644 --- a/src/coreclr/vm/datadescriptor/datadescriptor.inc +++ b/src/coreclr/vm/datadescriptor/datadescriptor.inc @@ -1319,11 +1319,6 @@ CDAC_TYPE_SIZE(sizeof(ExternalMethodFrame)) CDAC_TYPE_FIELD(ExternalMethodFrame, T_POINTER, Indirection, cdac_data::Indirection) CDAC_TYPE_END(ExternalMethodFrame) -CDAC_TYPE_BEGIN(PInvokeCalliFrame) -CDAC_TYPE_SIZE(sizeof(PInvokeCalliFrame)) -CDAC_TYPE_FIELD(PInvokeCalliFrame, T_POINTER, VASigCookiePtr, cdac_data::VASigCookiePtr) -CDAC_TYPE_END(PInvokeCalliFrame) - CDAC_TYPE_BEGIN(DynamicHelperFrame) CDAC_TYPE_SIZE(sizeof(DynamicHelperFrame)) CDAC_TYPE_FIELD(DynamicHelperFrame, T_INT32, DynamicHelperFrameFlags, cdac_data::DynamicHelperFrameFlags) diff --git a/src/coreclr/vm/dllimport.cpp b/src/coreclr/vm/dllimport.cpp index 613e358e862fc1..0b2ed0147ed692 100644 --- a/src/coreclr/vm/dllimport.cpp +++ b/src/coreclr/vm/dllimport.cpp @@ -238,6 +238,15 @@ class ILStubState m_fSetLastError = fSetLastError; } + // Records a failure that must be reported when the stub is called instead of while it is being + // generated. Marshaling still runs, but FinishEmit discards its output and emits only the throw. + void SetInteropExceptionInfo(RuntimeExceptionKind kind, UINT resID) + { + LIMITED_METHOD_CONTRACT; + + m_slIL.SetInteropExceptionInfo(kind, resID); + } + // We use three stub linkers to generate IL stubs. The pre linker is the main one. It does all the marshaling and // then calls the target method. The post return linker is only used to unmarshal the return value after we return // from the target method. The post linker handles all the unmarshaling for by ref arguments and clean-up. It @@ -258,6 +267,12 @@ class ILStubState _ASSERTE(m_dwStubFlags == dwStubFlags); } + void SetCalliTargetArgIndex(UINT uArgIdx) + { + WRAPPER_NO_CONTRACT; + m_slIL.SetCalliTargetArgIndex(uArgIdx); + } + virtual void MarshalReturn(MarshalInfo* pInfo, int argOffset) { CONTRACTL @@ -755,7 +770,14 @@ class ILStubState CORJIT_FLAGS jitFlags(CORJIT_FLAGS::CORJIT_FLAG_IL_STUB); - if (m_slIL.HasInteropParamExceptionInfo()) + if (m_slIL.HasInteropExceptionInfo()) + { + // This code will not use the secret parameter, so we do not + // tell the JIT to bother with it. + m_slIL.ClearCode(); + m_slIL.GenerateInteropException(pcsMarshal); + } + else if (m_slIL.HasInteropParamExceptionInfo()) { // This code will not use the secret parameter, so we do not // tell the JIT to bother with it. @@ -771,10 +793,10 @@ class ILStubState // Forward delegate stubs get all the context they need in 'this' so they // don't use the secret parameter. } - else if (SF_IsForwardPInvokeStub(m_dwStubFlags) - && !SF_IsCALLIStub(m_dwStubFlags) && !SF_IsVarArgStub(m_dwStubFlags)) + else if (SF_IsForwardPInvokeStub(m_dwStubFlags) && !SF_IsVarArgStub(m_dwStubFlags)) { - // Regular PInvokes don't use the secret parameter + // Regular PInvokes and unmanaged CALLI stubs don't use the secret parameter. + // Unmanaged CALLI stubs receive the native target as their last argument. } else { @@ -1863,7 +1885,10 @@ PInvokeStubLinker::PInvokeStubLinker( m_dwRetValLocalNum(-1), m_ErrorResID(-1), m_ErrorParamIdx(-1), + m_ExceptionKind(kLastException), + m_ExceptionResID(-1), m_iLCIDParamIdx(iLCIDParamIdx), + m_uCalliTargetArgIdx(0), m_dwStubFlags(dwStubFlags) { STANDARD_VM_CONTRACT; @@ -1915,6 +1940,13 @@ void PInvokeStubLinker::SetCallingConvention(CorInfoCallConvExtension unmngCallC } } +void PInvokeStubLinker::SetCalliTargetArgIndex(UINT uArgIdx) +{ + LIMITED_METHOD_CONTRACT; + _ASSERTE(SF_IsCALLIStub(m_dwStubFlags)); + m_uCalliTargetArgIdx = uArgIdx; +} + void PInvokeStubLinker::EmitSetArgMarshalIndex(ILCodeStream* pcsEmit, UINT uArgIdx) { WRAPPER_NO_CONTRACT; @@ -2057,6 +2089,39 @@ void PInvokeStubLinker::GenerateInteropParamException(ILCodeStream* pcsEmit) pcsEmit->EmitTHROW(); } +void PInvokeStubLinker::SetInteropExceptionInfo(RuntimeExceptionKind kind, UINT resID) +{ + LIMITED_METHOD_CONTRACT; + + // only keep the first one + if (HasInteropExceptionInfo()) + { + return; + } + + m_ExceptionKind = kind; + m_ExceptionResID = resID; +} + +bool PInvokeStubLinker::HasInteropExceptionInfo() +{ + LIMITED_METHOD_CONTRACT; + + return m_ExceptionResID != (UINT)-1; +} + +void PInvokeStubLinker::GenerateInteropException(ILCodeStream* pcsEmit) +{ + STANDARD_VM_CONTRACT; + + pcsEmit->EmitLDC(static_cast(m_ExceptionKind)); + pcsEmit->EmitLDC(m_ExceptionResID); + pcsEmit->EmitCALL(METHOD__STUBHELPERS__THROW_INTEROP_EXCEPTION, 2, 0); + + pcsEmit->EmitLDNULL(); + pcsEmit->EmitTHROW(); +} + #ifdef FEATURE_COMINTEROP DWORD PInvokeStubLinker::GetTargetInterfacePointerLocalNum() { @@ -2360,9 +2425,9 @@ void PInvokeStubLinker::DoPInvoke(ILCodeStream *pcsEmit, DWORD dwStubFlags, Meth #endif // FEATURE_COMINTEROP else if (SF_IsCALLIStub(dwStubFlags)) // unmanaged CALLI { - // for managed-to-unmanaged CALLI that requires marshaling, the target is passed - // as the secret argument to the stub by GenericPInvokeCalliHelper - EmitLoadStubContext(pcsEmit, dwStubFlags); + // For managed-to-unmanaged CALLI that requires marshaling, the JIT passes the native + // target as the last argument of the stub (see code:CEEInfo::convertPInvokeCalliToCall). + pcsEmit->EmitLDARG(m_uCalliTargetArgIdx); } else if (SF_IsVarArgStub(dwStubFlags)) // vararg P/Invoke { @@ -2436,8 +2501,8 @@ void PInvokeStubLinker::EmitLogNativeArgument(ILCodeStream* pslILEmit, DWORD dwP if (SF_IsCALLIStub(m_dwStubFlags)) { - // get the secret argument via intrinsic - pslILEmit->EmitCALL(METHOD__STUBHELPERS__GET_STUB_CONTEXT, 0, 1); + // The native target is passed as the last argument of the stub. + pslILEmit->EmitLDARG(m_uCalliTargetArgIdx); } else { @@ -3576,6 +3641,7 @@ static MarshalInfo::MarshalType DoMarshalReturnValue(MetaSig& msig, CorNativeLinkType nlType, CorNativeLinkFlags nlFlags, UINT argidx, // this is used for reverse pinvoke hresult swapping + int numArgs, // number of marshaled arguments ILStubState* pss, int argOffset, DWORD dwStubFlags, @@ -3621,7 +3687,7 @@ static MarshalInfo::MarshalType DoMarshalReturnValue(MetaSig& msig, nlFlags, FALSE, argidx, - msig.NumFixedArgs(), + numArgs, SF_IsBestFit(dwStubFlags), SF_IsThrowOnUnmappableChar(dwStubFlags), TRUE, @@ -3810,6 +3876,16 @@ static COR_ILMETHOD_DECODER* CreatePInvokeStubWorker( int numArgs = msig.NumFixedArgs(); + if (SF_IsCALLIStub(dwStubFlags)) + { + // The last argument of an unmanaged CALLI stub is the native target, which the JIT + // supplies from the top of the evaluation stack at the original calli site. It is not + // part of the native signature, so exclude it from marshaling. + _ASSERTE(numArgs > 0); + numArgs--; + pss->SetCalliTargetArgIndex((UINT)numArgs); + } + // thiscall must have at least one parameter (the "this") if (fThisCall && numArgs == 0) COMPlusThrow(kMarshalDirectiveException, IDS_EE_NDIRECT_BADNATL_THISCALL); @@ -3938,6 +4014,7 @@ static COR_ILMETHOD_DECODER* CreatePInvokeStubWorker( nlType, nlFlags, argidx, + numArgs, pss, argOffset, dwStubFlags, @@ -4543,7 +4620,12 @@ static void CreatePInvokeStubAccessMetadata( unmgdCallConv == CorInfoCallConvExtension::Fastcall || unmgdCallConv == CorInfoCallConvExtension::FastcallMemberFunction) { - COMPlusThrow(kTypeLoadException, IDS_INVALID_PINVOKE_CALLCONV); + // For an unmanaged CALLI stub the caller has already recorded this so that it is + // reported when the stub is called - see code:TryGetCalliStubCallConv. + if (!SF_IsCALLIStub(*pdwStubFlags)) + { + COMPlusThrow(kTypeLoadException, IDS_INVALID_PINVOKE_CALLCONV); + } } } @@ -5386,7 +5468,9 @@ namespace if (SF_IsCALLIStub(dwStubFlags) && PInvoke::MarshalingRequired(NULL, pStubMD->GetSigPointer(), pSigDesc->m_pModule, &pSigDesc->m_typeContext)) { - COMPlusThrow(kMarshalDirectiveException, IDS_EE_BADMARSHAL_GENERICS_RESTRICTION); + // The stub is created while the calli's caller is jitted, so report + // this when the stub is called instead of failing that compilation. + pss->SetInteropExceptionInfo(kMarshalDirectiveException, IDS_EE_BADMARSHAL_GENERICS_RESTRICTION); } // We don't want to support generic varargs, so block it else if (SF_IsVarArgStub(dwStubFlags)) @@ -5478,6 +5562,7 @@ namespace return pStubMD; } + } MethodDesc* PInvoke::CreateCLRToNativeILStub( @@ -6031,10 +6116,10 @@ EXTERN_C void* PInvokeImportWorker(PInvokeMethodDesc* pMD) } //=========================================================================== -// Support for Pinvoke Calli instruction +// Support for vararg Pinvoke and the Pinvoke Calli instruction // //=========================================================================== -static void GetILStubForCalli(VASigCookie* pVASigCookie, MethodDesc* pMD) +static void GetILStubForVarargPInvoke(VASigCookie* pVASigCookie, MethodDesc* pMD) { CONTRACTL { @@ -6043,7 +6128,8 @@ static void GetILStubForCalli(VASigCookie* pVASigCookie, MethodDesc* pMD) ENTRY_POINT; MODE_ANY; PRECONDITION(CheckPointer(pVASigCookie)); - PRECONDITION(CheckPointer(pMD, NULL_OK)); + PRECONDITION(CheckPointer(pMD)); + PRECONDITION(pMD->IsPInvoke()); } CONTRACTL_END; @@ -6061,126 +6147,314 @@ static void GetILStubForCalli(VASigCookie* pVASigCookie, MethodDesc* pMD) GCX_PREEMP(); - Signature signature = pVASigCookie->signature; - CorInfoCallConvExtension unmgdCallConv = CorInfoCallConvExtension::Managed; + _ASSERTE(!pMD->IsAsyncMethod()); + + DWORD dwStubFlags = PINVOKESTUB_FL_BESTFIT | PINVOKESTUB_FL_CONVSIGASVARARG; - DWORD dwStubFlags = PINVOKESTUB_FL_BESTFIT; + // vararg P/Invoke must be cdecl + CorInfoCallConvExtension unmgdCallConv = CorInfoCallConvExtension::C; - if (pMD == NULL) + PInvokeStaticSigInfo sigInfo(pMD); + CorNativeLinkFlags nlFlags = sigInfo.GetLinkFlags(); + CorNativeLinkType nlType = sigInfo.GetCharSet(); + + StubSigDesc sigDesc(pMD, pVASigCookie->signature, pVASigCookie->pModule, pVASigCookie->pLoaderModule); + sigDesc.InitTypeContext(pVASigCookie->classInst, pVASigCookie->methodInst); + + MethodDesc* pStubMD = PInvoke::CreateCLRToNativeILStub(&sigDesc, + nlType, + nlFlags, + unmgdCallConv, + dwStubFlags); + + pTempILStub = JitILStub(pStubMD); + + InterlockedCompareExchangeT(&pVASigCookie->pPInvokeILStub, + pTempILStub, + (PCODE)NULL); + + UNINSTALL_UNWIND_AND_CONTINUE_HANDLER; + UNINSTALL_MANAGED_EXCEPTION_DISPATCHER; + UNINSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_FRAME; +} + +// Build the managed signature of the IL stub that implements an unmanaged CALLI call site. +// +// The stub signature is the call site signature with a managed (default) calling convention +// and an extra trailing native int parameter that carries the unmanaged target. The target is +// the value at the top of the evaluation stack at the calli site, so appending it as the last +// parameter lets the JIT rewrite the calli into a direct call to the stub. +// +// The calling convention of the call site is not part of this signature. It does not need to be: +// the IL stub cache keys on it separately through PInvokeStubHashBlob::m_unmgdCallConv and +// PInvokeStubHashBlob::m_StubFlags, so call sites that differ only in calling convention or in +// SuppressGCTransition still get distinct stubs. +// +// A call site with an instance 'this' cannot be described by any unmanaged calling convention and +// is rejected, but the rejection is reported when the stub is called. That requires a stub the JIT +// can call, so the 'this' becomes an explicit leading native int parameter and the stub pops the +// call site correctly. +static void BuildCalliILStubSignature( + const Signature& calliSignature, + SigBuilder* pSigBuilder) +{ + STANDARD_VM_CONTRACT; + + SigParser sigParser = calliSignature.CreateSigParser(); + + uint32_t callConv; + IfFailThrow(sigParser.GetCallingConvInfo(&callConv)); + + // Only signatures that describe a standalone method signature get here. + _ASSERTE((callConv & IMAGE_CEE_CS_CALLCONV_GENERIC) == 0); + + bool hasImplicitThis = (callConv & IMAGE_CEE_CS_CALLCONV_HASTHIS) != 0 && + (callConv & IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS) == 0; + + uint32_t numArgs; + IfFailThrow(sigParser.GetData(&numArgs)); + + pSigBuilder->AppendByte(IMAGE_CEE_CS_CALLCONV_DEFAULT); + pSigBuilder->AppendData(numArgs + (hasImplicitThis ? 2 : 1)); + + // The return type, copied verbatim. + PCCOR_SIGNATURE pRetTypeStart = sigParser.GetPtr(); + IfFailThrow(sigParser.SkipExactlyOne()); + PCCOR_SIGNATURE pRetTypeEnd = sigParser.GetPtr(); + pSigBuilder->AppendBlob((PVOID)pRetTypeStart, (DWORD)(pRetTypeEnd - pRetTypeStart)); + + // The instance pointer, if the call site has an implicit 'this', ahead of the declared parameters. + // For EXPLICITTHIS signatures the this parameter is already part of numArgs, so no extra parameter + // is added. + if (hasImplicitThis) { - dwStubFlags |= PINVOKESTUB_FL_UNMANAGED_CALLI; + pSigBuilder->AppendElementType(ELEMENT_TYPE_I); + } - // need to convert the CALLI signature to stub signature with managed calling convention - BYTE callConv = MetaSig::GetCallingConvention(signature); + // The declared parameters, copied verbatim. + PCCOR_SIGNATURE pArgsStart = sigParser.GetPtr(); + for (uint32_t i = 0; i < numArgs; i++) + { + IfFailThrow(sigParser.SkipExactlyOne()); + } + PCCOR_SIGNATURE pArgsEnd = sigParser.GetPtr(); + pSigBuilder->AppendBlob((PVOID)pArgsStart, (DWORD)(pArgsEnd - pArgsStart)); + + // The unmanaged target is the last parameter. + pSigBuilder->AppendElementType(ELEMENT_TYPE_I); +} - // Unmanaged calling convention indicates modopt should be read - if (callConv != IMAGE_CEE_CS_CALLCONV_UNMANAGED) +// A failure detected while classifying an unmanaged CALLI call site. It is reported when the stub +// is called rather than while the caller is being jitted, so it is carried alongside a usable +// calling convention that lets stub creation run to completion. +struct CalliStubDeferredError +{ + RuntimeExceptionKind Kind = kLastException; + UINT ResID = 0; + + bool IsSet() const { LIMITED_METHOD_CONTRACT; return Kind != kLastException; } + + void Set(RuntimeExceptionKind kind, UINT resID) + { + LIMITED_METHOD_CONTRACT; + // Only keep the first one. + if (!IsSet()) { - unmgdCallConv = (CorInfoCallConvExtension)callConv; + Kind = kind; + ResID = resID; } - else + } +}; + +// Determines the unmanaged calling convention of an unmanaged CALLI call site and the +// corresponding stub flags. Returns false if the signature does not describe a P/Invoke. +// +// A signature that describes a P/Invoke the runtime cannot express records the failure in +// *pError and still returns a usable calling convention, so that a stub can be created to report +// it when it is called. Only a signature that is not a standalone method signature at all - one +// the stub signature cannot even be built from - throws. +static bool TryGetCalliStubCallConv( + Module* pModule, + const Signature& calliSignature, + CorInfoCallConvExtension* pUnmgdCallConv, + DWORD* pdwStubFlags, + CalliStubDeferredError* pError) +{ + STANDARD_VM_CONTRACT; + + SigParser sigParser = calliSignature.CreateSigParser(); + uint32_t rawCallConv; + IfFailThrow(sigParser.GetCallingConvInfo(&rawCallConv)); + + switch (rawCallConv & IMAGE_CEE_CS_CALLCONV_MASK) + { + case IMAGE_CEE_CS_CALLCONV_C: + case IMAGE_CEE_CS_CALLCONV_STDCALL: + case IMAGE_CEE_CS_CALLCONV_THISCALL: + case IMAGE_CEE_CS_CALLCONV_FASTCALL: + *pUnmgdCallConv = (CorInfoCallConvExtension)(rawCallConv & IMAGE_CEE_CS_CALLCONV_MASK); + break; + + case IMAGE_CEE_CS_CALLCONV_UNMANAGED: { + // The unmanaged calling convention is encoded in modopts. CallConvBuilder builder; UINT errorResID; - HRESULT hr = CallConv::TryGetUnmanagedCallingConventionFromModOpt(GetScopeHandle(pVASigCookie->pModule), signature.GetRawSig(), signature.GetRawSigLen(), &builder, &errorResID); - if (FAILED(hr)) - COMPlusThrowHR(hr, errorResID); + HRESULT hr = CallConv::TryGetUnmanagedCallingConventionFromModOpt(GetScopeHandle(pModule), calliSignature.GetRawSig(), calliSignature.GetRawSigLen(), &builder, &errorResID); - unmgdCallConv = builder.GetCurrentCallConv(); - if (unmgdCallConv == CallConvBuilder::UnsetValue) + CorInfoCallConvExtension unmgdCallConv; + if (FAILED(hr)) { + // Report the failure the same way COMPlusThrowHR(hr, errorResID) would have: + // the exception kind comes from the HRESULT, not from the parser's failure mode. + pError->Set(EEException::GetKindFromHR(hr), errorResID); unmgdCallConv = CallConv::GetDefaultUnmanagedCallingConvention(); } - - if (builder.IsCurrentCallConvModSet(CallConvBuilder::CALL_CONV_MOD_SUPPRESSGCTRANSITION)) + else { - dwStubFlags |= PINVOKESTUB_FL_SUPPRESSGCTRANSITION; + unmgdCallConv = builder.GetCurrentCallConv(); + if (unmgdCallConv == CallConvBuilder::UnsetValue) + { + unmgdCallConv = CallConv::GetDefaultUnmanagedCallingConvention(); + } + + if (builder.IsCurrentCallConvModSet(CallConvBuilder::CALL_CONV_MOD_SUPPRESSGCTRANSITION)) + { + *pdwStubFlags |= PINVOKESTUB_FL_SUPPRESSGCTRANSITION; + } } + + *pUnmgdCallConv = unmgdCallConv; + break; } - LoaderHeap *pHeap = pVASigCookie->pLoaderModule->GetLoaderAllocator()->GetHighFrequencyHeap(); - PCOR_SIGNATURE new_sig = (PCOR_SIGNATURE)(void *)pHeap->AllocMem(S_SIZE_T(signature.GetRawSigLen())); - CopyMemory(new_sig, signature.GetRawSig(), signature.GetRawSigLen()); + case IMAGE_CEE_CS_CALLCONV_DEFAULT: + case IMAGE_CEE_CS_CALLCONV_VARARG: + case IMAGE_CEE_CS_CALLCONV_NATIVEVARARG: + case IMAGE_CEE_CS_CALLCONV_ASYNC: + // Managed call sites - including the runtime-internal native-vararg and async + // conventions used by IL stubs - are not P/Invokes. + return false; - // make the stub IMAGE_CEE_CS_CALLCONV_DEFAULT - *new_sig &= ~IMAGE_CEE_CS_CALLCONV_MASK; - *new_sig |= IMAGE_CEE_CS_CALLCONV_DEFAULT; + default: + // Not a method signature at all, so no stub signature can be built from it. + COMPlusThrowHR(COR_E_BADIMAGEFORMAT); + } - signature = Signature(new_sig, signature.GetRawSigLen()); + if ((rawCallConv & IMAGE_CEE_CS_CALLCONV_GENERIC) != 0) + { + // An unmanaged standalone signature is never generic, and its argument count is not where + // the stub signature builder expects it, so no stub can be built from it either. + COMPlusThrowHR(COR_E_BADIMAGEFORMAT); } - else + + // An unmanaged call site carrying an instance 'this' is not expressible as a stub, and the JIT + // cannot emit it inline either. Reject it the same way a P/Invoke declared on an instance method + // is rejected - see code:CreatePInvokeStubAccessMetadata. Note that a native member function is + // expressed as a static signature whose first parameter is the 'this' pointer, so this only + // rejects signatures that no calling convention can describe. + if ((rawCallConv & (IMAGE_CEE_CS_CALLCONV_HASTHIS | IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS)) != 0) { - _ASSERTE(pMD->IsPInvoke()); - dwStubFlags |= PINVOKESTUB_FL_CONVSIGASVARARG; + pError->Set(kInvalidProgramException, VLDTR_E_FMD_PINVOKENOTSTATIC); + } - // vararg P/Invoke must be cdecl - unmgdCallConv = CorInfoCallConvExtension::C; + // The JIT refuses to emit these inline, and stub creation cannot express them either. + if (*pUnmgdCallConv == CorInfoCallConvExtension::Managed || + *pUnmgdCallConv == CorInfoCallConvExtension::Fastcall || + *pUnmgdCallConv == CorInfoCallConvExtension::FastcallMemberFunction) + { + pError->Set(kTypeLoadException, IDS_INVALID_PINVOKE_CALLCONV); } - mdMethodDef md = mdMethodDefNil; - CorNativeLinkFlags nlFlags = nlfNone; - CorNativeLinkType nlType = nltAnsi; + return true; +} - if (pMD != NULL) +MethodDesc* PInvoke::CreateCalliILStub( + Module* pModule, + const Signature& calliSignature, + const SigTypeContext* pTypeContext, + bool fMustCreate) +{ + CONTRACTL { - _ASSERTE(pMD->IsPInvoke()); - _ASSERTE(!pMD->IsAsyncMethod()); - PInvokeStaticSigInfo sigInfo(pMD); - - md = pMD->GetMemberDef(); - nlFlags = sigInfo.GetLinkFlags(); - nlType = sigInfo.GetCharSet(); + STANDARD_VM_CHECK; + PRECONDITION(CheckPointer(pModule)); + PRECONDITION(CheckPointer(pTypeContext)); } + CONTRACTL_END; - StubSigDesc sigDesc(pMD, signature, pVASigCookie->pModule, pVASigCookie->pLoaderModule); - sigDesc.InitTypeContext(pVASigCookie->classInst, pVASigCookie->methodInst); + DWORD dwStubFlags = PINVOKESTUB_FL_BESTFIT | PINVOKESTUB_FL_UNMANAGED_CALLI; + CorInfoCallConvExtension unmgdCallConv; + CalliStubDeferredError deferredError; + if (!TryGetCalliStubCallConv(pModule, calliSignature, &unmgdCallConv, &dwStubFlags, &deferredError)) + return NULL; - MethodDesc* pStubMD = PInvoke::CreateCLRToNativeILStub(&sigDesc, - nlType, - nlFlags, - unmgdCallConv, - dwStubFlags); + // The generic context is stripped if the signature does not actually use it. That is + // required for correctness: built-in runtime marshalling is disallowed for generic + // signatures, and it also lets the stub be shared across instantiations. + SigTypeContext typeContext = *pTypeContext; + Module* pLoaderModule = pModule->GetLoaderModuleForSignature(calliSignature, &typeContext); - pTempILStub = JitILStub(pStubMD); + // If no marshaling is needed and nothing is wrong with the call site, let the caller emit the + // unmanaged call inline unless it explicitly asked for a stub. + if (!fMustCreate + && !deferredError.IsSet() + && !PInvoke::MarshalingRequired(NULL, calliSignature.CreateSigPointer(), pModule, &typeContext)) + { + return NULL; + } - InterlockedCompareExchangeT(&pVASigCookie->pPInvokeILStub, - pTempILStub, - (PCODE)NULL); + SigBuilder sigBuilder; + BuildCalliILStubSignature(calliSignature, &sigBuilder); - UNINSTALL_UNWIND_AND_CONTINUE_HANDLER; - UNINSTALL_MANAGED_EXCEPTION_DISPATCHER; - UNINSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_FRAME; -} + DWORD cbStubSig; + PCCOR_SIGNATURE pStubSig = (PCCOR_SIGNATURE)sigBuilder.GetSignature(&cbStubSig); -EXTERN_C void STDCALL VarargPInvokeStubWorker(TransitionBlock* pTransitionBlock, VASigCookie *pVASigCookie, MethodDesc *pMD) -{ - PreserveLastErrorHolder preserveLastError; + // The stub MethodDesc references this buffer for as long as it lives, so it has to be owned + // by the loader allocator the stub lives in. It is backed out below if the stub did not keep it. + AllocMemHolder pStubSigCopy(pLoaderModule->GetLoaderAllocator()->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(cbStubSig))); + memcpy(pStubSigCopy, pStubSig, cbStubSig); - STATIC_CONTRACT_THROWS; - STATIC_CONTRACT_GC_TRIGGERS; - STATIC_CONTRACT_MODE_COOPERATIVE; - STATIC_CONTRACT_ENTRY_POINT; + StubSigDesc sigDesc(NULL, Signature((PCCOR_SIGNATURE)(BYTE*)pStubSigCopy, cbStubSig), pModule, pLoaderModule); + sigDesc.InitTypeContext(typeContext.m_classInst, typeContext.m_methodInst); - MAKE_CURRENT_THREAD_AVAILABLE(); + int iLCIDArg = 0; + int numArgs = 0; + CreatePInvokeStubAccessMetadata(&sigDesc, unmgdCallConv, &dwStubFlags, &iLCIDArg, &numArgs); -#ifdef _DEBUG - Thread::ObjectRefFlush(CURRENT_THREAD); -#endif + int numParamTokens = numArgs + 1; + mdParamDef* pParamTokenArray = (mdParamDef*)_alloca(numParamTokens * sizeof(mdParamDef)); + CollateParamTokens(pModule->GetMDImport(), sigDesc.m_tkMethodDef, numArgs, pParamTokenArray); - PrestubMethodFrame frame(pTransitionBlock, pMD); - PrestubMethodFrame * pFrame = &frame; + PInvoke_ILStubState stubState(pModule, sigDesc.m_sig, &sigDesc.m_typeContext, dwStubFlags, unmgdCallConv, iLCIDArg, NULL); - pFrame->Push(CURRENT_THREAD); + if (deferredError.IsSet()) + { + // The call site cannot be marshaled. Generate a stub whose body throws so that the failure + // is reported when the call site executes rather than while its caller is jitted. + stubState.SetInteropExceptionInfo(deferredError.Kind, deferredError.ResID); + } - _ASSERTE(pVASigCookie == pFrame->GetVASigCookie()); - _ASSERTE(pMD == pFrame->GetFunction()); + MethodDesc* pStubMD = CreateInteropILStub( + &stubState, + &sigDesc, + nltAnsi, + nlfNone, + unmgdCallConv, + numParamTokens, + pParamTokenArray, + iLCIDArg); - GetILStubForCalli(pVASigCookie, pMD); + PCCOR_SIGNATURE pFinalSig; + DWORD cbFinalSig; + pStubMD->GetSig(&pFinalSig, &cbFinalSig); + if (pFinalSig == (PCCOR_SIGNATURE)(BYTE*)pStubSigCopy) + pStubSigCopy.SuppressRelease(); - pFrame->Pop(CURRENT_THREAD); + return pStubMD; } -EXTERN_C void STDCALL GenericPInvokeCalliStubWorker(TransitionBlock * pTransitionBlock, VASigCookie * pVASigCookie, PCODE pUnmanagedTarget) +EXTERN_C void STDCALL VarargPInvokeStubWorker(TransitionBlock* pTransitionBlock, VASigCookie *pVASigCookie, MethodDesc *pMD) { PreserveLastErrorHolder preserveLastError; @@ -6195,14 +6469,15 @@ EXTERN_C void STDCALL GenericPInvokeCalliStubWorker(TransitionBlock * pTransitio Thread::ObjectRefFlush(CURRENT_THREAD); #endif - PInvokeCalliFrame frame(pTransitionBlock, pVASigCookie, pUnmanagedTarget); - PInvokeCalliFrame * pFrame = &frame; + PrestubMethodFrame frame(pTransitionBlock, pMD); + PrestubMethodFrame * pFrame = &frame; pFrame->Push(CURRENT_THREAD); _ASSERTE(pVASigCookie == pFrame->GetVASigCookie()); + _ASSERTE(pMD == pFrame->GetFunction()); - GetILStubForCalli(pVASigCookie, NULL); + GetILStubForVarargPInvoke(pVASigCookie, pMD); pFrame->Pop(CURRENT_THREAD); } diff --git a/src/coreclr/vm/dllimport.h b/src/coreclr/vm/dllimport.h index 7e1fc77d5513e7..3c5b42b2ce92dd 100644 --- a/src/coreclr/vm/dllimport.h +++ b/src/coreclr/vm/dllimport.h @@ -127,6 +127,16 @@ class PInvoke CorInfoCallConvExtension unmgdCallConv, DWORD dwStubFlags); // PInvokeStubFlags + // Creates the IL stub that marshals an unmanaged calli call site described by + // calliSignature. Returns NULL if the call site does not describe an unmanaged call, or if + // no marshaling is required and fMustCreate is false (in which case the caller - the JIT - + // can emit the unmanaged call inline instead). + static MethodDesc* CreateCalliILStub( + Module* pModule, + const Signature& calliSignature, + const SigTypeContext* pTypeContext, + bool fMustCreate); + static COR_ILMETHOD_DECODER* CreatePInvokeMethodIL( PInvokeMethodDesc* pMD, DynamicResolver** ppResolver); @@ -461,6 +471,9 @@ class PInvokeStubLinker : public ILStubLinker void SetCallingConvention(CorInfoCallConvExtension unmngCallConv, BOOL fIsVarArg); + // For unmanaged CALLI stubs, the native target is passed in as the last argument of the stub. + void SetCalliTargetArgIndex(UINT uArgIdx); + void Begin(DWORD dwStubFlags); void End(DWORD dwStubFlags); void DoPInvoke(ILCodeStream *pcsEmit, DWORD dwStubFlags, MethodDesc * pStubMD); @@ -491,6 +504,19 @@ class PInvokeStubLinker : public ILStubLinker void SetInteropParamExceptionInfo(UINT resID, UINT paramIdx); bool HasInteropParamExceptionInfo(); + + // Records an interop failure that is not tied to a single parameter and that must be reported + // when the stub is called rather than while it is being generated. The stub body becomes a + // single throw - see code:PInvokeStubLinker::GenerateInteropException. + void SetInteropExceptionInfo(RuntimeExceptionKind kind, UINT resID); + bool HasInteropExceptionInfo(); + void GenerateInteropException(ILCodeStream* pcsEmit); + + DWORD GetStubFlags() const + { + LIMITED_METHOD_CONTRACT; + return m_dwStubFlags; + } bool TargetHasThis() { return m_targetHasThis == TRUE; @@ -557,7 +583,10 @@ class PInvokeStubLinker : public ILStubLinker UINT m_ErrorResID; UINT m_ErrorParamIdx; + RuntimeExceptionKind m_ExceptionKind; + UINT m_ExceptionResID; int m_iLCIDParamIdx; + UINT m_uCalliTargetArgIdx; DWORD m_dwStubFlags; }; diff --git a/src/coreclr/vm/frames.cpp b/src/coreclr/vm/frames.cpp index 202864af98c65b..09e03980527329 100644 --- a/src/coreclr/vm/frames.cpp +++ b/src/coreclr/vm/frames.cpp @@ -328,16 +328,9 @@ void Frame::Log() { STRESS_LOG3(LF_STUBS, LL_INFO1000000, "STUBS: In Stub with Frame %p assoc Method %pM FrameType = %pV\n", this, method, *((void**) this)); - char buff[64]; const char* frameType; if (GetFrameIdentifier() == FrameIdentifier::PrestubMethodFrame) frameType = "PreStub"; - else if (GetFrameIdentifier() == FrameIdentifier::PInvokeCalliFrame) - { - sprintf_s(buff, ARRAY_SIZE(buff), "PInvoke CALLI target" FMT_ADDR, - DBG_ADDR(((PInvokeCalliFrame*)this)->GetPInvokeCalliTarget())); - frameType = buff; - } else if (GetFrameIdentifier() == FrameIdentifier::StubDispatchFrame) frameType = "StubDispatch"; else if (GetFrameIdentifier() == FrameIdentifier::ExternalMethodFrame) @@ -1706,37 +1699,6 @@ void TransitionFrame::PromoteCallerStackUsingGCRefMap(promote_func* fn, ScanCont } } -void PInvokeCalliFrame::PromoteCallerStack(promote_func* fn, ScanContext* sc) -{ - WRAPPER_NO_CONTRACT; - - LOG((LF_GC, INFO3, " Promoting CALLI caller Arguments\n" )); - - // get the signature - VASigCookie *varArgSig = GetVASigCookie(); - if (varArgSig->signature.IsEmpty()) - { - return; - } - - SigTypeContext typeContext(varArgSig->classInst, varArgSig->methodInst); - MetaSig msig(varArgSig->signature, - varArgSig->pModule, - &typeContext); - PromoteCallerStackHelper(fn, sc, NULL, &msig); -} - -#ifndef DACCESS_COMPILE -PInvokeCalliFrame::PInvokeCalliFrame(TransitionBlock * pTransitionBlock, VASigCookie * pVASigCookie, PCODE pUnmanagedTarget) - : FramedMethodFrame(FrameIdentifier::PInvokeCalliFrame, pTransitionBlock, NULL) -{ - LIMITED_METHOD_CONTRACT; - - m_pVASigCookie = pVASigCookie; - m_pUnmanagedTarget = pUnmanagedTarget; -} -#endif // #ifndef DACCESS_COMPILE - #if defined (_DEBUG) && !defined (DACCESS_COMPILE) // For IsProtectedByGCFrame, we need to know whether a given object ref is protected // by a TransitionFrame. Since GCScanRoots for those frames are diff --git a/src/coreclr/vm/frames.h b/src/coreclr/vm/frames.h index cd84feb5d49f3a..d59872c9e9afcb 100644 --- a/src/coreclr/vm/frames.h +++ b/src/coreclr/vm/frames.h @@ -64,9 +64,6 @@ // | | that generates a full-fledged frame. // | | // | | -// | +-PInvokeCalliFrame - protects arguments when a call to GetILStubForCalli is made -// | | to get or create IL stub for an unmanaged CALLI -// | | // | +-PrestubMethodFrame - represents a call to a prestub // | | // | +-StubDispatchFrame - represents a call into the virtual call stub manager @@ -114,10 +111,9 @@ Delegate over a native function pointer: goes through an IL stub). Calli: - The same as P/Invoke. - PInvokeCalliFrame is erected in stub generated by GenerateGetStubForPInvokeCalli - before calling to GetILStubForCalli which generates the IL stub. This happens only - the first time a call via the corresponding VASigCookie is made. + The same as P/Invoke. The IL stub is created when the caller is JIT-compiled + (see code:CEEInfo::convertPInvokeCalliToCall) and receives the unmanaged target + as its last argument. ClrToCom: Eventing: The stub does not erect any frames explicitly and calls back into managed code. @@ -1303,76 +1299,6 @@ struct cdac_data static constexpr size_t MethodDescPtr = offsetof(FramedMethodFrame, m_pMD); }; -//------------------------------------------------------------------------ -// This represents a call from a helper to GetILStubForCalli -//------------------------------------------------------------------------ - -typedef DPTR(class PInvokeCalliFrame) PTR_PInvokeCalliFrame; - -class PInvokeCalliFrame : public FramedMethodFrame -{ - PTR_VASigCookie m_pVASigCookie; - PCODE m_pUnmanagedTarget; - -public: - PInvokeCalliFrame(TransitionBlock * pTransitionBlock, VASigCookie * pVASigCookie, PCODE pUnmanagedTarget); - - void GcScanRoots_Impl(promote_func *fn, ScanContext* sc) - { - WRAPPER_NO_CONTRACT; - FramedMethodFrame::GcScanRoots_Impl(fn, sc); - PromoteCallerStack(fn, sc); - } - - void PromoteCallerStack(promote_func* fn, ScanContext* sc); - - // not a method - MethodDesc *GetFunction_Impl() - { - LIMITED_METHOD_DAC_CONTRACT; - return NULL; - } - - int GetFrameType_Impl() - { - LIMITED_METHOD_DAC_CONTRACT; - return TYPE_INTERCEPTION; - } - - PCODE GetPInvokeCalliTarget() - { - LIMITED_METHOD_CONTRACT; - return m_pUnmanagedTarget; - } - - PTR_VASigCookie GetVASigCookie() - { - LIMITED_METHOD_CONTRACT; - return m_pVASigCookie; - } - -#ifdef TARGET_X86 - void UpdateRegDisplay_Impl(const PREGDISPLAY, bool updateFloats = false); -#endif // TARGET_X86 - - BOOL TraceFrame_Impl(Thread *thread, BOOL fromPatch, - TraceDestination *trace, REGDISPLAY *regs) - { - WRAPPER_NO_CONTRACT; - - trace->InitForUnmanaged(GetPInvokeCalliTarget()); - return TRUE; - } - - friend struct ::cdac_data; -}; - -template <> -struct cdac_data -{ - static constexpr size_t VASigCookiePtr = offsetof(PInvokeCalliFrame, m_pVASigCookie); -}; - // Some context-related forwards. #ifdef FEATURE_HIJACK //------------------------------------------------------------------------ diff --git a/src/coreclr/vm/i386/asmhelpers.S b/src/coreclr/vm/i386/asmhelpers.S index 29b76b1f220ffb..b2284ad1c7cdf2 100644 --- a/src/coreclr/vm/i386/asmhelpers.S +++ b/src/coreclr/vm/i386/asmhelpers.S @@ -399,53 +399,6 @@ LOCAL_LABEL(GoCallVarargWorker): jmp C_FUNC(VarargPInvokeStub) NESTED_END VarargPInvokeStub, _TEXT -// ========================================================================== -// Invoked for marshaling-required unmanaged CALLI calls as a stub. -// EAX - the unmanaged target -// ECX, EDX - arguments -// EBX - the VASigCookie -// -LEAF_ENTRY GenericPInvokeCalliHelper, _TEXT - - cmp dword ptr [ebx + VASigCookie__StubOffset], 0 - jz LOCAL_LABEL(GoCallCalliWorker) - - // Stub is already prepared, just jump to it - jmp dword ptr [ebx + VASigCookie__StubOffset] - -LOCAL_LABEL(GoCallCalliWorker): - // - // call the stub generating worker - // target ptr in EAX, VASigCookie ptr in EBX - // - - STUB_PROLOG - - mov esi, esp - - // save target - push eax - - #define STACK_ALIGN_PADDING 4 - sub esp, STACK_ALIGN_PADDING // pass stack aligned to 0x10 - push eax // unmanaged target - push ebx // pVaSigCookie (first stack argument) - push esi // pTransitionBlock - - CHECK_STACK_ALIGNMENT - call C_FUNC(GenericPInvokeCalliStubWorker) - add esp, STACK_ALIGN_PADDING // restore alignment, callee pop args - #undef STACK_ALIGN_PADDING - - // restore target - pop eax - - STUB_EPILOG - - // jump back to the helper - this time it won't come back here as the stub already exists - jmp C_FUNC(GenericPInvokeCalliHelper) -LEAF_END GenericPInvokeCalliHelper, _TEXT - #ifdef FEATURE_READYTORUN NESTED_ENTRY DynamicHelperArgsStub, _TEXT, NoHandler .cfi_def_cfa_offset 16 diff --git a/src/coreclr/vm/i386/asmhelpers.asm b/src/coreclr/vm/i386/asmhelpers.asm index f160c43c27b485..3dd14e72f7dfe2 100644 --- a/src/coreclr/vm/i386/asmhelpers.asm +++ b/src/coreclr/vm/i386/asmhelpers.asm @@ -38,7 +38,6 @@ EXTERN __alloca_probe:PROC EXTERN _PInvokeImportWorker@4:PROC EXTERN _VarargPInvokeStubWorker@12:PROC -EXTERN _GenericPInvokeCalliStubWorker@12:PROC EXTERN _PreStubWorker@8:PROC EXTERN _TheUMEntryPrestubWorker@4:PROC @@ -701,49 +700,6 @@ GoCallVarargWorker: _VarargPInvokeStub@0 endp -;========================================================================== -; Invoked for marshaling-required unmanaged CALLI calls as a stub. -; EAX - the unmanaged target -; ECX, EDX - arguments -; EBX - the VASigCookie -; -_GenericPInvokeCalliHelper@0 proc public - - cmp dword ptr [ebx + VASigCookie__StubOffset], 0 - jz GoCallCalliWorker - - ; Stub is already prepared, just jump to it - jmp dword ptr [ebx + VASigCookie__StubOffset] - -GoCallCalliWorker: - ; - ; call the stub generating worker - ; target ptr in EAX, VASigCookie ptr in EBX - ; - - STUB_PROLOG - - mov esi, esp - - ; save target - push eax - - push eax ; unmanaged target - push ebx ; pVaSigCookie (first stack argument) - push esi ; pTransitionBlock - - call _GenericPInvokeCalliStubWorker@12 - - ; restore target - pop eax - - STUB_EPILOG - - ; jump back to the helper - this time it won't come back here as the stub already exists - jmp _GenericPInvokeCalliHelper@0 - -_GenericPInvokeCalliHelper@0 endp - ifdef FEATURE_READYTORUN ;========================================================================== _DelayLoad_MethodCall@0 proc public diff --git a/src/coreclr/vm/i386/cgenx86.cpp b/src/coreclr/vm/i386/cgenx86.cpp index 1500b80f90030f..bc2daec3b7cab1 100644 --- a/src/coreclr/vm/i386/cgenx86.cpp +++ b/src/coreclr/vm/i386/cgenx86.cpp @@ -372,23 +372,6 @@ void HijackFrame::UpdateRegDisplay_Impl(const PREGDISPLAY pRD, bool updateFloats #endif // FEATURE_HIJACK -void PInvokeCalliFrame::UpdateRegDisplay_Impl(const PREGDISPLAY pRD, bool updateFloats) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - SUPPORTS_DAC; - } - CONTRACTL_END; - - VASigCookie *pVASigCookie = GetVASigCookie(); - UpdateRegDisplayHelper(pRD, pVASigCookie->sizeOfArgs); - - LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK PInvokeCalliFrame::UpdateRegDisplay_Impl(ip:%p, sp:%p)\n", pRD->ControlPC, pRD->SP)); -} - #ifndef UNIX_X86_ABI void TailCallFrame::UpdateRegDisplay_Impl(const PREGDISPLAY pRD, bool updateFloats) { diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 63f5380933ad5d..fa5cedc118a717 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -6649,6 +6649,13 @@ DWORD CEEInfo::getMethodAttribsInternal (CORINFO_METHOD_HANDLE ftn) /* Function marked as not inlineable */ result |= CORINFO_FLG_DONT_INLINE; } + else if (pMD->IsILStub()) + { + // IL stubs have no metadata and their IL is only materialized while the stub itself is + // being compiled, so they can never be inlined into their callers. Reporting this here + // keeps the JIT from asking for the stub's IL (see code:CEEInfo::canInline). + result |= CORINFO_FLG_DONT_INLINE; + } // AggressiveInlining only makes sense for IL methods. else if (pMD->IsIL() && IsMiAggressiveInlining(ilMethodImplAttribs)) { @@ -10194,17 +10201,6 @@ bool CEEInfo::pInvokeMarshalingRequired(CORINFO_METHOD_HANDLE method, CORINFO_SI return result; } -/*********************************************************************/ -// Generate a cookie based on the signature that would needs to be passed -// to CORINFO_HELP_PINVOKE_CALLI -LPVOID CEEInfo::GetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig, - void **ppIndirection) -{ - WRAPPER_NO_CONTRACT; - - return getVarArgsHandle(szMetaSig, NULL, ppIndirection); -} - // Check any constraints on method type arguments bool CEEInfo::satisfiesMethodConstraints( CORINFO_CLASS_HANDLE parent, @@ -15484,9 +15480,94 @@ CORINFO_METHOD_HANDLE CEEJitInfo::getAsyncResumptionStub(void** entryPoint) return CORINFO_METHOD_HANDLE(result); } +//--------------------------------------------------------------------------------------- +// +// Optionally converts an unmanaged calli call site into a call to an IL stub that performs +// the argument marshalling and the managed/native transition. +// +// The IL stub is created (and cached in the IL stub cache by signature) here, but it is only +// JIT-compiled the first time it is called - we cannot JIT it while we are jitting the caller. +// +// Arguments: +// pResolvedToken - the token of the calli call site. Only token, tokenScope and +// tokenContext are valid on entry. On success, hMethod is filled in +// with the IL stub. +// fMustConvert - true if the JIT cannot emit an inline P/Invoke at this call site and +// therefore requires the conversion. +// +// Return Value: +// true if the call site was converted to a call to an IL stub. +// bool CEEInfo::convertPInvokeCalliToCall(CORINFO_RESOLVED_TOKEN * pResolvedToken, bool fMustConvert) { - return false; + CONTRACTL { + THROWS; + GC_TRIGGERS; + MODE_PREEMPTIVE; + } CONTRACTL_END; + + bool result = false; + + JIT_TO_EE_TRANSITION(); + + CORINFO_MODULE_HANDLE scopeHnd = pResolvedToken->tokenScope; + + SigPointer sig{}; + bool canConvert = true; + + if (IsDynamicScope(scopeHnd)) + { + // The calli that dispatches to the unmanaged target inside a marshalling stub is the + // P/Invoke itself and must stay inline. Those call sites always use + // TOKEN_ILSTUB_TARGET_SIG, and their IL is owned either by an IL stub MethodDesc or, + // for a plain P/Invoke, directly by the PInvokeMethodDesc. + if (pResolvedToken->token == (mdToken)TOKEN_ILSTUB_TARGET_SIG) + { + canConvert = false; + } + else + { + sig = GetDynamicResolver(scopeHnd)->ResolveSignature(pResolvedToken->token); + } + } + else + { + Module* pModule = (Module*)scopeHnd; + + PCCOR_SIGNATURE pSig = NULL; + uint32_t cbSig = 0; + IfFailThrow(pModule->GetMDImport()->GetSigFromToken( + (mdSignature)pResolvedToken->token, + (ULONG*)&cbSig, + &pSig)); + sig = SigPointer{ pSig, cbSig }; + } + + if (canConvert) + { + PCCOR_SIGNATURE pSig; + uint32_t cbSig; + sig.GetSignature(&pSig, &cbSig); + + SigTypeContext typeContext; + GetTypeContext(pResolvedToken->tokenContext, &typeContext); + + MethodDesc* pStubMD = PInvoke::CreateCalliILStub( + GetModule(scopeHnd), + Signature(pSig, cbSig), + &typeContext, + fMustConvert); + + if (pStubMD != NULL) + { + pResolvedToken->hMethod = (CORINFO_METHOD_HANDLE)pStubMD; + result = true; + } + } + + EE_TO_JIT_TRANSITION(); + + return result; } void CEEInfo::updateEntryPointForTailCall(CORINFO_CONST_LOOKUP* entryPoint) diff --git a/src/coreclr/vm/loongarch64/pinvokestubs.S b/src/coreclr/vm/loongarch64/pinvokestubs.S index 28a33a9db07c7c..8052571d1786a7 100644 --- a/src/coreclr/vm/loongarch64/pinvokestubs.S +++ b/src/coreclr/vm/loongarch64/pinvokestubs.S @@ -11,7 +11,7 @@ // // Params :- // $FuncPrefix : prefix of the function name for the stub -// Eg. VarargPinvoke, GenericPInvokeCalli +// Eg. VarargPinvoke // $VASigCookieReg : register which contains the VASigCookie // $SaveFPArgs : "Yes" or "No" . For varidic functions FP Args are not present in FP regs // So need not save FP Args registers for vararg Pinvoke @@ -156,16 +156,6 @@ LEAF_END JIT_PInvokeEnd, _TEXT PINVOKE_STUB VarargPInvokeStub, VarargPInvokeGenILStub, VarargPInvokeStubWorker, $a0, $t2, 0 -// ------------------------------------------------------------------ -// GenericPInvokeCalliHelper & GenericPInvokeCalliGenILStub -// Helper for generic pinvoke calli instruction -// -// in: -// $t3 = VASigCookie* -// $t2 = Unmanaged target -// -PINVOKE_STUB GenericPInvokeCalliHelper, GenericPInvokeCalliGenILStub, GenericPInvokeCalliStubWorker, $t3, $t2, 1 - //// ------------------------------------------------------------------ //// VarargPInvokeStub_RetBuffArg & VarargPInvokeGenILStub_RetBuffArg //// Vararg PInvoke Stub when the method has a hidden return buffer arg diff --git a/src/coreclr/vm/mlinfo.cpp b/src/coreclr/vm/mlinfo.cpp index b89c18524f1efc..80132ad83c1497 100644 --- a/src/coreclr/vm/mlinfo.cpp +++ b/src/coreclr/vm/mlinfo.cpp @@ -2084,6 +2084,14 @@ VOID MarshalInfo::EmitOrThrowInteropParamException(PInvokeStubLinker* psl, BOOL } #endif // FEATURE_COMINTEROP + // An unmanaged CALLI stub is created while the calli's caller is being jitted, so its failures + // have to be reported when the stub is called rather than failing that compilation. + if (SF_IsCALLIStub(psl->GetStubFlags())) + { + psl->SetInteropParamExceptionInfo(resID, paramIdx); + return; + } + ThrowInteropParamException(resID, paramIdx); } diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index 41e7624c50466f..6f594713732591 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -500,6 +500,7 @@ static const Entry s_QCall[] = DllImportEntry(StubHelpers_CreateCustomMarshaler) DllImportEntry(StubHelpers_CreateLayoutClassMarshalStubs) DllImportEntry(StubHelpers_ThrowInteropParamException) + DllImportEntry(StubHelpers_ThrowInteropException) DllImportEntry(StubHelpers_MarshalToManagedVaList) DllImportEntry(StubHelpers_MarshalToUnmanagedVaList) DllImportEntry(StubHelpers_ValidateObject) diff --git a/src/coreclr/vm/riscv64/pinvokestubs.S b/src/coreclr/vm/riscv64/pinvokestubs.S index 027eec6cb01dfa..1d99a5b1b6d932 100644 --- a/src/coreclr/vm/riscv64/pinvokestubs.S +++ b/src/coreclr/vm/riscv64/pinvokestubs.S @@ -11,7 +11,7 @@ // // Params :- // FuncPrefix : prefix of the function name for the stub -// Eg. VarargPinvoke, GenericPInvokeCalli +// Eg. VarargPinvoke // VASigCookieReg : register which contains the VASigCookie // SaveFPArgs : "Yes" or "No" . For varidic functions FP Args are not present in FP regs // So need not save FP Args registers for vararg Pinvoke @@ -150,16 +150,6 @@ LEAF_END JIT_PInvokeEnd, _TEXT PINVOKE_STUB VarargPInvokeStub, VarargPInvokeGenILStub, VarargPInvokeStubWorker, a0, t2, 0 -// ------------------------------------------------------------------ -// GenericPInvokeCalliHelper & GenericPInvokeCalliGenILStub -// Helper for generic pinvoke calli instruction -// -// in: -// t3 = VASigCookie* -// t2 = Unmanaged target -// -PINVOKE_STUB GenericPInvokeCalliHelper, GenericPInvokeCalliGenILStub, GenericPInvokeCalliStubWorker, t3, t2, 1 - //// ------------------------------------------------------------------ //// VarargPInvokeStub_RetBuffArg & VarargPInvokeGenILStub_RetBuffArg //// Vararg PInvoke Stub when the method has a hidden return buffer arg diff --git a/src/coreclr/vm/stubhelpers.cpp b/src/coreclr/vm/stubhelpers.cpp index f122b797fad4fa..4c76dd47dea8b7 100644 --- a/src/coreclr/vm/stubhelpers.cpp +++ b/src/coreclr/vm/stubhelpers.cpp @@ -502,6 +502,18 @@ extern "C" void QCALLTYPE StubHelpers_ThrowInteropParamException(INT resID, INT END_QCALL; } +// Throws an interop failure that was detected while the stub was being generated. Stubs that are +// created while their caller is being jitted report their failures this way so that a call site +// that is never executed does not fail the compilation of the method containing it. +extern "C" void QCALLTYPE StubHelpers_ThrowInteropException(INT exceptionKind, INT resID) +{ + QCALL_CONTRACT; + + BEGIN_QCALL; + COMPlusThrow(static_cast(exceptionKind), static_cast(resID)); + END_QCALL; +} + #ifdef PROFILING_SUPPORTED extern "C" void* QCALLTYPE StubHelpers_ProfilerBeginTransitionCallback(MethodDesc* pTargetMD) { diff --git a/src/coreclr/vm/stubhelpers.h b/src/coreclr/vm/stubhelpers.h index 33d791390844a7..02627224198c2c 100644 --- a/src/coreclr/vm/stubhelpers.h +++ b/src/coreclr/vm/stubhelpers.h @@ -57,6 +57,7 @@ extern "C" void QCALLTYPE InterfaceMarshaler_ValidateComVisibilityForIUnknown(IU #endif extern "C" void QCALLTYPE StubHelpers_ThrowInteropParamException(INT resID, INT paramIdx); +extern "C" void QCALLTYPE StubHelpers_ThrowInteropException(INT exceptionKind, INT resID); extern "C" void QCALLTYPE StubHelpers_MarshalToManagedVaList(va_list va, VARARGS* pArgIterator); extern "C" void QCALLTYPE StubHelpers_MarshalToUnmanagedVaList(va_list va, DWORD cbVaListSize, const VARARGS* pArgIterator); diff --git a/src/coreclr/vm/stubmgr.cpp b/src/coreclr/vm/stubmgr.cpp index 87c559554f2ec0..4deafc3008d689 100644 --- a/src/coreclr/vm/stubmgr.cpp +++ b/src/coreclr/vm/stubmgr.cpp @@ -1702,6 +1702,105 @@ static PCODE GetLateBoundCOMTarget(Object *pThis, CLRToCOMCallInfo *pCLRToCOMCal } #endif // FEATURE_COMINTEROP +// Reads the incoming value of the index-th integer argument register from a context captured at +// a managed method's entry point. The registers are indexed the way a TransitionBlock lays them +// out, which is the same order the ArgIterator reports. +static bool TryGetArgumentRegister(T_CONTEXT *pContext, unsigned index, TADDR *pValue) +{ + LIMITED_METHOD_CONTRACT; + +#if defined(TARGET_X86) || defined(TARGET_AMD64) || defined(TARGET_ARM) || defined(TARGET_ARM64) + if (index >= NUM_ARGUMENT_REGISTERS) + return false; + + ArgumentRegisters regs; +#if defined(TARGET_X86) + regs.ECX = (INT32)pContext->Ecx; + regs.EDX = (INT32)pContext->Edx; +#elif defined(TARGET_AMD64) +#ifdef UNIX_AMD64_ABI + regs.RDI = (INT_PTR)pContext->Rdi; + regs.RSI = (INT_PTR)pContext->Rsi; + regs.RDX = (INT_PTR)pContext->Rdx; + regs.RCX = (INT_PTR)pContext->Rcx; + regs.R8 = (INT_PTR)pContext->R8; + regs.R9 = (INT_PTR)pContext->R9; +#else + regs.RCX = (INT_PTR)pContext->Rcx; + regs.RDX = (INT_PTR)pContext->Rdx; + regs.R8 = (INT_PTR)pContext->R8; + regs.R9 = (INT_PTR)pContext->R9; +#endif // UNIX_AMD64_ABI +#elif defined(TARGET_ARM) + regs.r[0] = (INT32)pContext->R0; + regs.r[1] = (INT32)pContext->R1; + regs.r[2] = (INT32)pContext->R2; + regs.r[3] = (INT32)pContext->R3; +#elif defined(TARGET_ARM64) + for (unsigned i = 0; i < NUM_ARGUMENT_REGISTERS; i++) + regs.x[i] = (INT64)pContext->X[i]; +#endif + + static_assert(sizeof(ArgumentRegisters) == NUM_ARGUMENT_REGISTERS * sizeof(TADDR), + "Argument registers are expected to be a flat array of pointer sized values"); + + *pValue = ((TADDR*)®s)[index]; + return true; +#else + // Not implemented for this architecture. + return false; +#endif +} + +// Recovers the unmanaged target of an unmanaged CALLI stub from a context captured at the stub's +// entry point. The target is passed as the last argument of the stub - see +// code:BuildCalliILStubSignature. +static bool TryGetCalliStubTarget(MethodDesc *pStubMD, T_CONTEXT *pContext, PCODE *pTarget) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + PRECONDITION(pStubMD->AsDynamicMethodDesc()->IsPInvokeCalliStub()); + } + CONTRACTL_END; + + MetaSig msig(pStubMD); + ArgIterator argit(&msig); + + int targetOffset = TransitionBlock::InvalidOffset; + int argOffset; + while ((argOffset = argit.GetNextOffset()) != TransitionBlock::InvalidOffset) + { + targetOffset = argOffset; + } + + if (targetOffset == TransitionBlock::InvalidOffset) + return false; + + if (TransitionBlock::IsArgumentRegisterOffset(targetOffset)) + { + TADDR value; + if (!TryGetArgumentRegister(pContext, TransitionBlock::GetArgumentIndexFromOffset(targetOffset), &value)) + return false; + + *pTarget = (PCODE)value; + return true; + } + + // The incoming stack arguments start right past the return address when the call instruction + // pushes it on the stack, and at the stack pointer itself when it is passed in a link register. +#if defined(TARGET_X86) || defined(TARGET_AMD64) + TADDR stackArgs = GetSP(pContext) + sizeof(TADDR); +#else + TADDR stackArgs = GetSP(pContext); +#endif + + *pTarget = *(PCODE*)(stackArgs + (targetOffset - TransitionBlock::GetOffsetOfArgs())); + return true; +} + BOOL ILStubManager::TraceManager(Thread *thread, TraceDestination *trace, T_CONTEXT *pContext, @@ -1755,8 +1854,14 @@ BOOL ILStubManager::TraceManager(Thread *thread, } else if (pStubMD->IsPInvokeCalliStub()) { - // This is unmanaged CALLI stub, the argument is the target - target = (PCODE)arg; + // This is an unmanaged CALLI stub. The native target is passed as its last argument + // rather than in a hidden argument, so it has to be read out of the incoming argument + // location that the calling convention assigned to it. + if (!TryGetCalliStubTarget(pStubMD, pContext, &target)) + { + LOG((LF_CORDB, LL_INFO1000, "ILSM::TraceManager: Unmanaged CALLI stub - could not locate the target\n")); + return FALSE; + } LOG((LF_CORDB, LL_INFO10000, "ILSM::TraceManager: Unmanaged CALLI case %p\n", target)); trace->InitForUnmanaged(target); @@ -1904,7 +2009,7 @@ BOOL PInvokeStubManager::DoTraceStub(PCODE stubStartAddress, #endif // !DACCESS_COMPILE } -// This is used to recognize VarargPInvokeStub, and GenericPInvokeCalliHelper. +// This is used to recognize VarargPInvokeStub. #ifndef DACCESS_COMPILE @@ -1951,12 +2056,6 @@ BOOL InteropDispatchStubManager::CheckIsStub_Internal(PCODE stubStartAddress) { return true; } - - if (stubStartAddress == GetEEFuncEntryPoint(GenericPInvokeCalliHelper)) - { - return true; - } - #endif // !DACCESS_COMPILE return false; } @@ -2018,18 +2117,6 @@ BOOL InteropDispatchStubManager::TraceManager(Thread *thread, trace->InitForUnmanaged(target); #endif //defined(TARGET_ARM64) && defined(__APPLE__) } - else if (stubIP == GetEEFuncEntryPoint(GenericPInvokeCalliHelper)) - { -#if defined(TARGET_ARM64) && defined(__APPLE__) - //On ARM64 Mac, we cannot put a breakpoint inside of GenericPInvokeCalliHelper - LOG((LF_CORDB, LL_INFO10000, "IDSM::TraceManager: Skipping on arm64-macOS\n")); - return FALSE; -#else - PCODE target = (PCODE)arg; - LOG((LF_CORDB, LL_INFO10000, "IDSM::TraceManager: Unmanaged CALLI case %p\n", target)); - trace->InitForUnmanaged(target); -#endif //defined(TARGET_ARM64) && defined(__APPLE__) - } return TRUE; } diff --git a/src/coreclr/vm/stubmgr.h b/src/coreclr/vm/stubmgr.h index 8418ca5f1e7681..1b9bcc2eaa8d57 100644 --- a/src/coreclr/vm/stubmgr.h +++ b/src/coreclr/vm/stubmgr.h @@ -641,7 +641,6 @@ class PInvokeStubManager : public StubManager // This is used to recognize // VarargPInvokeStub() -// GenericPInvokeCalliHelper() typedef VPTR(class InteropDispatchStubManager) PTR_InteropDispatchStubManager; class InteropDispatchStubManager : public StubManager diff --git a/src/coreclr/vm/wasm/helpers.cpp b/src/coreclr/vm/wasm/helpers.cpp index f97cffffb87021..3532f8d4659b2c 100644 --- a/src/coreclr/vm/wasm/helpers.cpp +++ b/src/coreclr/vm/wasm/helpers.cpp @@ -705,11 +705,6 @@ EXTERN_C VOID STDCALL ResetCurrentContext() { } -extern "C" void STDCALL GenericPInvokeCalliHelper(void) -{ - PORTABILITY_ASSERT("GenericPInvokeCalliHelper is not implemented on wasm"); -} - // Does the pinvoke frame transition; the naked wrappers below have already set the wasm // __stack_pointer global to sp so it is safe to run native code here. EXTERN_C void JIT_PInvokeBeginImpl(void* sp, InlinedCallFrame* pFrame)