From 922e12be4299410d3b26e79c2c182e8e95bec932 Mon Sep 17 00:00:00 2001 From: Santiago Fernandez Madero Date: Wed, 16 Dec 2020 15:53:04 -0800 Subject: [PATCH] Revert "Enable non-blittable struct returns on UnmanagedCallersOnly (#45625)" This reverts commit c64861b3bb0b3e31f70aabee875ded8cfd4dbaf7. --- .../superpmi/superpmi-shared/lwmlist.h | 4 +- .../superpmi-shared/methodcontext.cpp | 101 +++--- .../superpmi/superpmi-shared/methodcontext.h | 12 +- .../superpmi-shim-collector/icorjitinfo.cpp | 13 +- .../superpmi-shim-counter/icorjitinfo.cpp | 8 +- .../superpmi-shim-simple/icorjitinfo.cpp | 8 +- .../ToolBox/superpmi/superpmi/icorjitinfo.cpp | 11 +- src/coreclr/debug/daccess/nidump.cpp | 1 + src/coreclr/inc/corhdr.h | 26 +- src/coreclr/inc/corinfo.h | 57 ++-- src/coreclr/inc/icorjitinfoimpl_generated.h | 6 +- src/coreclr/inc/jiteeversionguid.h | 10 +- src/coreclr/jit/ICorJitInfo_API_wrapper.hpp | 8 +- src/coreclr/jit/codegencommon.cpp | 25 +- src/coreclr/jit/codegenxarch.cpp | 3 +- src/coreclr/jit/compiler.cpp | 40 ++- src/coreclr/jit/compiler.h | 10 +- src/coreclr/jit/flowgraph.cpp | 1 - src/coreclr/jit/gcencode.cpp | 4 +- src/coreclr/jit/importer.cpp | 90 +++--- src/coreclr/jit/lclvars.cpp | 12 +- src/coreclr/jit/lower.cpp | 4 +- src/coreclr/jit/lsrabuild.cpp | 3 +- src/coreclr/jit/morph.cpp | 3 +- src/coreclr/jit/target.h | 18 ++ .../tools/Common/JitInterface/CorInfoBase.cs | 6 +- .../tools/Common/JitInterface/CorInfoImpl.cs | 192 +++-------- .../tools/Common/JitInterface/CorInfoTypes.cs | 28 +- .../ThunkGenerator/ThunkInput.txt | 18 +- .../tools/aot/jitinterface/jitinterface.h | 8 +- src/coreclr/vm/amd64/cgencpu.h | 14 + src/coreclr/vm/arm/cgencpu.h | 11 + src/coreclr/vm/arm64/cgencpu.h | 9 + src/coreclr/vm/dllimport.cpp | 77 ++++- src/coreclr/vm/dllimportcallback.cpp | 248 +++++++------- src/coreclr/vm/dllimportcallback.h | 4 +- src/coreclr/vm/i386/cgencpu.h | 15 + src/coreclr/vm/interpreter.cpp | 32 +- src/coreclr/vm/jitinterface.cpp | 302 +++++++----------- src/coreclr/vm/jitinterface.h | 14 +- src/coreclr/vm/method.cpp | 6 + src/coreclr/vm/method.hpp | 12 + src/coreclr/zap/zapinfo.cpp | 22 +- .../Miscellaneous/ThisCall/ThisCallTest.cs | 100 ------ 44 files changed, 725 insertions(+), 871 deletions(-) diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shared/lwmlist.h b/src/coreclr/ToolBox/superpmi/superpmi-shared/lwmlist.h index b400fbab0b29a8..8aca1b1634b834 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shared/lwmlist.h +++ b/src/coreclr/ToolBox/superpmi/superpmi-shared/lwmlist.h @@ -132,6 +132,7 @@ LWM(GetTypeForPrimitiveValueClass, DWORDLONG, DWORD) LWM(GetTypeForPrimitiveNumericClass, DWORDLONG, DWORD) LWM(GetUnboxedEntry, DWORDLONG, DLD); LWM(GetUnBoxHelper, DWORDLONG, DWORD) +LWM(GetUnmanagedCallConv, DWORDLONG, DWORD) LWM(GetVarArgsHandle, GetVarArgsHandleValue, DLDL) LWM(GetVars, DWORDLONG, Agnostic_GetVars) DENSELWM(HandleException, DWORD) @@ -148,13 +149,12 @@ LWM(IsValidToken, DLD, DWORD) LWM(IsValueClass, DWORDLONG, DWORD) LWM(MergeClasses, DLDL, DWORDLONG) LWM(IsMoreSpecificType, DLDL, DWORD) -LWM(PInvokeMarshalingRequired, MethodOrSigInfoValue, DWORD) +LWM(PInvokeMarshalingRequired, PInvokeMarshalingRequiredValue, DWORD) LWM(ResolveToken, Agnostic_CORINFO_RESOLVED_TOKENin, ResolveTokenValue) LWM(ResolveVirtualMethod, Agnostic_ResolveVirtualMethodKey, Agnostic_ResolveVirtualMethodResult) LWM(TryResolveToken, Agnostic_CORINFO_RESOLVED_TOKENin, TryResolveTokenValue) LWM(SatisfiesClassConstraints, DWORDLONG, DWORD) LWM(SatisfiesMethodConstraints, DLDL, DWORD) -LWM(GetUnmanagedCallConv, MethodOrSigInfoValue, DD) #undef LWM diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.cpp b/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.cpp index 5ee64d464855b4..6bca1e9f495fef 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.cpp @@ -1625,6 +1625,35 @@ bool MethodContext::repIsIntrinsicType(CORINFO_CLASS_HANDLE cls) return result; } +void MethodContext::recGetUnmanagedCallConv(CORINFO_METHOD_HANDLE method, CorInfoUnmanagedCallConv result) +{ + if (GetUnmanagedCallConv == nullptr) + GetUnmanagedCallConv = new LightWeightMap(); + + GetUnmanagedCallConv->Add((DWORDLONG)method, result); + DEBUG_REC(dmpGetUnmanagedCallConv((DWORDLONG)method, (DWORD)result)); +} +void MethodContext::dmpGetUnmanagedCallConv(DWORDLONG key, DWORD result) +{ + printf("GetUnmanagedCallConv key ftn-%016llX, value res-%u", key, result); +} +CorInfoUnmanagedCallConv MethodContext::repGetUnmanagedCallConv(CORINFO_METHOD_HANDLE method) +{ + if ((GetUnmanagedCallConv == nullptr) || (GetUnmanagedCallConv->GetIndex((DWORDLONG)method) == -1)) + { +#ifdef sparseMC + LogDebug("Sparse - repGetUnmanagedCallConv returning CORINFO_UNMANAGED_CALLCONV_STDCALL"); + return CORINFO_UNMANAGED_CALLCONV_STDCALL; +#else + LogException(EXCEPTIONCODE_MC, "Found a null GetUnmanagedCallConv. Probably missing a fatTrigger for %016llX.", + (DWORDLONG)method); +#endif + } + CorInfoUnmanagedCallConv result = (CorInfoUnmanagedCallConv)GetUnmanagedCallConv->Get((DWORDLONG)method); + DEBUG_REP(dmpGetUnmanagedCallConv((DWORDLONG)method, (DWORD)result)); + return result; +} + void MethodContext::recAsCorInfoType(CORINFO_CLASS_HANDLE cls, CorInfoType result) { if (AsCorInfoType == nullptr) @@ -3743,10 +3772,10 @@ void MethodContext::recPInvokeMarshalingRequired(CORINFO_METHOD_HANDLE method, bool result) { if (PInvokeMarshalingRequired == nullptr) - PInvokeMarshalingRequired = new LightWeightMap(); + PInvokeMarshalingRequired = new LightWeightMap(); - MethodOrSigInfoValue key; - ZeroMemory(&key, sizeof(MethodOrSigInfoValue)); // We use the input structs as a key and use memcmp to + PInvokeMarshalingRequiredValue key; + ZeroMemory(&key, sizeof(PInvokeMarshalingRequiredValue)); // We use the input structs as a key and use memcmp to // compare.. so we need to zero out padding too key.method = CastHandle(method); @@ -3757,7 +3786,7 @@ void MethodContext::recPInvokeMarshalingRequired(CORINFO_METHOD_HANDLE method, PInvokeMarshalingRequired->Add(key, (DWORD)result); DEBUG_REC(dmpPInvokeMarshalingRequired(key, (DWORD)result)); } -void MethodContext::dmpPInvokeMarshalingRequired(const MethodOrSigInfoValue& key, DWORD value) +void MethodContext::dmpPInvokeMarshalingRequired(const PInvokeMarshalingRequiredValue& key, DWORD value) { printf("PInvokeMarshalingRequired key mth-%016llX scp-%016llX sig-%u, value res-%u", key.method, key.scope, key.pSig_Index, value); @@ -3768,9 +3797,9 @@ bool MethodContext::repPInvokeMarshalingRequired(CORINFO_METHOD_HANDLE method, C if (PInvokeMarshalingRequired == nullptr) // so when we replay checked on free, we throw from lwm return TRUE; // TODO-Cleanup: hackish... - MethodOrSigInfoValue key; - ZeroMemory(&key, sizeof(MethodOrSigInfoValue)); // We use the input structs as a key and use memcmp to - // compare.. so we need to zero out padding too + PInvokeMarshalingRequiredValue key; + ZeroMemory(&key, sizeof(PInvokeMarshalingRequiredValue)); // We use the input structs as a key and use memcmp to + // compare.. so we need to zero out padding too key.method = CastHandle(method); key.pSig_Index = (DWORD)PInvokeMarshalingRequired->Contains((unsigned char*)callSiteSig->pSig, callSiteSig->cbSig); @@ -3782,61 +3811,7 @@ bool MethodContext::repPInvokeMarshalingRequired(CORINFO_METHOD_HANDLE method, C return value; } -void MethodContext::recGetUnmanagedCallConv(CORINFO_METHOD_HANDLE method, - CORINFO_SIG_INFO* callSiteSig, - CorInfoCallConvExtension result, - bool suppressGCTransitionResult) -{ - if (GetUnmanagedCallConv == nullptr) - GetUnmanagedCallConv = new LightWeightMap(); - - MethodOrSigInfoValue key; - ZeroMemory(&key, sizeof(MethodOrSigInfoValue)); // We use the input structs as a key and use memcmp to - // compare.. so we need to zero out padding too - - key.method = CastHandle(method); - key.pSig_Index = (DWORD)PInvokeMarshalingRequired->AddBuffer((unsigned char*)callSiteSig->pSig, callSiteSig->cbSig); - key.cbSig = (DWORD)callSiteSig->cbSig; - key.scope = CastHandle(callSiteSig->scope); - - GetUnmanagedCallConv->Add(key, { (DWORD)result, (DWORD)suppressGCTransitionResult }); - DEBUG_REC(dmpGetUnmanagedCallConv(key, { (DWORD)result, (DWORD)suppressGCTransitionResult })); -} -void MethodContext::dmpGetUnmanagedCallConv(const MethodOrSigInfoValue& key, DD value) -{ - printf("GetUnmanagedCallConv key mth-%016llX scp-%016llX sig-%u, value res-%u,%u", key.method, key.scope, - key.pSig_Index, value.A, value.B); -} -// Note the jit interface implementation seems to only care about scope and pSig from callSiteSig -CorInfoCallConvExtension MethodContext::repGetUnmanagedCallConv(CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* callSiteSig, bool* pSuppressGCTransition) -{ - if (GetUnmanagedCallConv == nullptr) - { -#ifdef sparseMC - LogDebug("Sparse - repGetUnmanagedCallConv returning CorInfoCallConvExtension::Managed"); - return CorInfoCallConvExtension::Managed; -#else - LogException(EXCEPTIONCODE_MC, "Found a null GetUnmGetUnmanagedCallConvanagedCallConv. Probably missing a fatTrigger for %016llX.", - CastHandle(method)); -#endif - } - - MethodOrSigInfoValue key; - ZeroMemory(&key, sizeof(MethodOrSigInfoValue)); // We use the input structs as a key and use memcmp to - // compare.. so we need to zero out padding too - - key.method = CastHandle(method); - key.pSig_Index = (DWORD)GetUnmanagedCallConv->Contains((unsigned char*)callSiteSig->pSig, callSiteSig->cbSig); - key.cbSig = (DWORD)callSiteSig->cbSig; - key.scope = CastHandle(callSiteSig->scope); - - DD value = GetUnmanagedCallConv->Get(key); - DEBUG_REP(dmpGetUnmanagedCallConv(key, value)); - *pSuppressGCTransition = value.B != 0; - return (CorInfoCallConvExtension)value.A; -} - -void MethodContext::recFindSig(CORINFO_MODULE_HANDLE moduleHandle, +void MethodContext::recFindSig(CORINFO_MODULE_HANDLE module, unsigned sigTOK, CORINFO_CONTEXT_HANDLE context, CORINFO_SIG_INFO* sig) @@ -5316,7 +5291,7 @@ void MethodContext::recGetLikelyClass(CORINFO_METHOD_HANDLE ftnHnd, CORINFO_CLAS } void MethodContext::dmpGetLikelyClass(const Agnostic_GetLikelyClass& key, const Agnostic_GetLikelyClassResult& value) { - printf("GetLikelyClass key ftn-%016llX base-%016llX il-%u, class-%016llX likelihood-%u numberOfClasses-%u", + printf("GetLikelyClass key ftn-%016llX base-%016llX il-%u, class-%016llX likelihood-%u numberOfClasses-%u", key.ftnHnd, key.baseHnd, key.ilOffset, value.classHnd, value.likelihood, value.numberOfClasses); } CORINFO_CLASS_HANDLE MethodContext::repGetLikelyClass(CORINFO_METHOD_HANDLE ftnHnd, CORINFO_CLASS_HANDLE baseHnd, UINT32 ilOffset, UINT32* pLikelihood, UINT32* pNumberOfClasses) diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.h b/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.h index 666c771830adc7..1678a1069447ad 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.h +++ b/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.h @@ -318,7 +318,7 @@ class MethodContext DWORD sigTOK; DWORDLONG context; }; - struct MethodOrSigInfoValue + struct PInvokeMarshalingRequiredValue { DWORDLONG method; DWORD pSig_Index; @@ -730,6 +730,10 @@ class MethodContext void dmpGetIntrinsicID(DWORDLONG key, DD value); CorInfoIntrinsics repGetIntrinsicID(CORINFO_METHOD_HANDLE method, bool* pMustExpand); + void recGetUnmanagedCallConv(CORINFO_METHOD_HANDLE method, CorInfoUnmanagedCallConv result); + void dmpGetUnmanagedCallConv(DWORDLONG key, DWORD result); + CorInfoUnmanagedCallConv repGetUnmanagedCallConv(CORINFO_METHOD_HANDLE method); + void recAsCorInfoType(CORINFO_CLASS_HANDLE cls, CorInfoType result); void dmpAsCorInfoType(DWORDLONG key, DWORD value); CorInfoType repAsCorInfoType(CORINFO_CLASS_HANDLE cls); @@ -999,13 +1003,9 @@ class MethodContext CORINFO_CLASS_HANDLE repEmbedClassHandle(CORINFO_CLASS_HANDLE handle, void** ppIndirection); void recPInvokeMarshalingRequired(CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* callSiteSig, bool result); - void dmpPInvokeMarshalingRequired(const MethodOrSigInfoValue& key, DWORD value); + void dmpPInvokeMarshalingRequired(const PInvokeMarshalingRequiredValue& key, DWORD value); bool repPInvokeMarshalingRequired(CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* callSiteSig); - void recGetUnmanagedCallConv(CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* callSiteSig, CorInfoCallConvExtension result, bool suppressGCTransitionResult); - void dmpGetUnmanagedCallConv(const MethodOrSigInfoValue& key, DD value); - CorInfoCallConvExtension repGetUnmanagedCallConv(CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* callSiteSig, bool* pSuppressGCTransition); - void recFindSig(CORINFO_MODULE_HANDLE module, unsigned sigTOK, CORINFO_CONTEXT_HANDLE context, diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp b/src/coreclr/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp index b001578311c6c2..90462003479c5c 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp @@ -289,15 +289,12 @@ bool interceptor_ICJI::isIntrinsicType(CORINFO_CLASS_HANDLE classHnd) return temp; } -// return the entry point calling convention for any of the following -// - a P/Invoke -// - a method marked with UnmanagedCallersOnly -// - a function pointer with the CORINFO_CALLCONV_UNMANAGED calling convention. -CorInfoCallConvExtension interceptor_ICJI::getUnmanagedCallConv(CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* callSiteSig, bool* pSuppressGCTransition) +// return the unmanaged calling convention for a PInvoke +CorInfoUnmanagedCallConv interceptor_ICJI::getUnmanagedCallConv(CORINFO_METHOD_HANDLE method) { mc->cr->AddCall("getUnmanagedCallConv"); - CorInfoCallConvExtension temp = original_ICorJitInfo->getUnmanagedCallConv(method, callSiteSig, pSuppressGCTransition); - mc->recGetUnmanagedCallConv(method, callSiteSig, temp, *pSuppressGCTransition); + CorInfoUnmanagedCallConv temp = original_ICorJitInfo->getUnmanagedCallConv(method); + mc->recGetUnmanagedCallConv(method, temp); return temp; } @@ -2049,7 +2046,7 @@ HRESULT interceptor_ICJI::getMethodBlockCounts(CORINFO_METHOD_HANDLE ftnHnd, // Get the likely implementing class for a virtual call or interface call made by ftnHnd // at the indicated IL offset. baseHnd is the interface class or base class for the method -// being called. +// being called. CORINFO_CLASS_HANDLE interceptor_ICJI::getLikelyClass(CORINFO_METHOD_HANDLE ftnHnd, CORINFO_CLASS_HANDLE baseHnd, UINT32 ilOffset, diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp b/src/coreclr/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp index 359a4bf55523c2..bb32bdf36a393c 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp @@ -162,13 +162,11 @@ bool interceptor_ICJI::isIntrinsicType( return original_ICorJitInfo->isIntrinsicType(classHnd); } -CorInfoCallConvExtension interceptor_ICJI::getUnmanagedCallConv( - CORINFO_METHOD_HANDLE method, - CORINFO_SIG_INFO* callSiteSig, - bool* pSuppressGCTransition) +CorInfoUnmanagedCallConv interceptor_ICJI::getUnmanagedCallConv( + CORINFO_METHOD_HANDLE method) { mcs->AddCall("getUnmanagedCallConv"); - return original_ICorJitInfo->getUnmanagedCallConv(method, callSiteSig, pSuppressGCTransition); + return original_ICorJitInfo->getUnmanagedCallConv(method); } bool interceptor_ICJI::pInvokeMarshalingRequired( diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp b/src/coreclr/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp index 22c019ab80c4e1..64c3e0e9e85809 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp @@ -144,12 +144,10 @@ bool interceptor_ICJI::isIntrinsicType( return original_ICorJitInfo->isIntrinsicType(classHnd); } -CorInfoCallConvExtension interceptor_ICJI::getUnmanagedCallConv( - CORINFO_METHOD_HANDLE method, - CORINFO_SIG_INFO* callSiteSig, - bool* pSuppressGCTransition) +CorInfoUnmanagedCallConv interceptor_ICJI::getUnmanagedCallConv( + CORINFO_METHOD_HANDLE method) { - return original_ICorJitInfo->getUnmanagedCallConv(method, callSiteSig, pSuppressGCTransition); + return original_ICorJitInfo->getUnmanagedCallConv(method); } bool interceptor_ICJI::pInvokeMarshalingRequired( diff --git a/src/coreclr/ToolBox/superpmi/superpmi/icorjitinfo.cpp b/src/coreclr/ToolBox/superpmi/superpmi/icorjitinfo.cpp index c210e187985164..0bbbd1f0e2532d 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi/icorjitinfo.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi/icorjitinfo.cpp @@ -218,14 +218,11 @@ bool MyICJI::isIntrinsicType(CORINFO_CLASS_HANDLE classHnd) return jitInstance->mc->repIsIntrinsicType(classHnd) ? true : false; } -// return the entry point calling convention for any of the following -// - a P/Invoke -// - a method marked with UnmanagedCallersOnly -// - a function pointer with the CORINFO_CALLCONV_UNMANAGED calling convention. -CorInfoCallConvExtension MyICJI::getUnmanagedCallConv(CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* callSiteSig, bool* pSuppressGCTransition) +// return the unmanaged calling convention for a PInvoke +CorInfoUnmanagedCallConv MyICJI::getUnmanagedCallConv(CORINFO_METHOD_HANDLE method) { jitInstance->mc->cr->AddCall("getUnmanagedCallConv"); - return jitInstance->mc->repGetUnmanagedCallConv(method, callSiteSig, pSuppressGCTransition); + return jitInstance->mc->repGetUnmanagedCallConv(method); } // return if any marshaling is required for PInvoke methods. Note that @@ -1806,7 +1803,7 @@ HRESULT MyICJI::getMethodBlockCounts(CORINFO_METHOD_HANDLE ftnHnd, // Get the likely implementing class for a virtual call or interface call made by ftnHnd // at the indicated IL offset. baseHnd is the interface class or base class for the method -// being called. +// being called. CORINFO_CLASS_HANDLE MyICJI::getLikelyClass(CORINFO_METHOD_HANDLE ftnHnd, CORINFO_CLASS_HANDLE baseHnd, UINT32 ilOffset, diff --git a/src/coreclr/debug/daccess/nidump.cpp b/src/coreclr/debug/daccess/nidump.cpp index 0cfa3951ba5ee1..e59fc9195954f8 100644 --- a/src/coreclr/debug/daccess/nidump.cpp +++ b/src/coreclr/debug/daccess/nidump.cpp @@ -7350,6 +7350,7 @@ static NativeImageDumper::EnumMnemonics g_NDirectFlags[] = NDF_ENTRY(kStdCall), NDF_ENTRY(kThisCall), NDF_ENTRY(kIsQCall), + NDF_ENTRY(kStdCallWithRetBuf), #undef NDF_ENTRY }; NativeImageDumper::EnumMnemonics NativeImageDumper::s_IMDFlags[] = diff --git a/src/coreclr/inc/corhdr.h b/src/coreclr/inc/corhdr.h index fd3dc4735de5fb..c410f4026de6fa 100644 --- a/src/coreclr/inc/corhdr.h +++ b/src/coreclr/inc/corhdr.h @@ -954,21 +954,11 @@ typedef enum CorSerializationType // Calling convention flags. // -typedef enum CorUnmanagedCallingConvention -{ - IMAGE_CEE_UNMANAGED_CALLCONV_C = 0x1, - IMAGE_CEE_UNMANAGED_CALLCONV_STDCALL = 0x2, - IMAGE_CEE_UNMANAGED_CALLCONV_THISCALL = 0x3, - IMAGE_CEE_UNMANAGED_CALLCONV_FASTCALL = 0x4, -} CorUnmanagedCallingConvention; typedef enum CorCallingConvention { IMAGE_CEE_CS_CALLCONV_DEFAULT = 0x0, - IMAGE_CEE_CS_CALLCONV_C = IMAGE_CEE_UNMANAGED_CALLCONV_C, - IMAGE_CEE_CS_CALLCONV_STDCALL = IMAGE_CEE_UNMANAGED_CALLCONV_STDCALL, - IMAGE_CEE_CS_CALLCONV_THISCALL = IMAGE_CEE_UNMANAGED_CALLCONV_THISCALL, - IMAGE_CEE_CS_CALLCONV_FASTCALL = IMAGE_CEE_UNMANAGED_CALLCONV_FASTCALL, + IMAGE_CEE_CS_CALLCONV_VARARG = 0x5, IMAGE_CEE_CS_CALLCONV_FIELD = 0x6, IMAGE_CEE_CS_CALLCONV_LOCAL_SIG = 0x7, @@ -989,6 +979,20 @@ typedef enum CorCallingConvention #define IMAGE_CEE_CS_CALLCONV_INSTANTIATION IMAGE_CEE_CS_CALLCONV_GENERICINST +typedef enum CorUnmanagedCallingConvention +{ + IMAGE_CEE_UNMANAGED_CALLCONV_C = 0x1, + IMAGE_CEE_UNMANAGED_CALLCONV_STDCALL = 0x2, + IMAGE_CEE_UNMANAGED_CALLCONV_THISCALL = 0x3, + IMAGE_CEE_UNMANAGED_CALLCONV_FASTCALL = 0x4, + + IMAGE_CEE_CS_CALLCONV_C = IMAGE_CEE_UNMANAGED_CALLCONV_C, + IMAGE_CEE_CS_CALLCONV_STDCALL = IMAGE_CEE_UNMANAGED_CALLCONV_STDCALL, + IMAGE_CEE_CS_CALLCONV_THISCALL = IMAGE_CEE_UNMANAGED_CALLCONV_THISCALL, + IMAGE_CEE_CS_CALLCONV_FASTCALL = IMAGE_CEE_UNMANAGED_CALLCONV_FASTCALL, + +} CorUnmanagedCallingConvention; + typedef enum CorArgType { diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index 815b5ac39f68a7..9992adfdb21a16 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -669,11 +669,10 @@ enum CorInfoCallConv // These correspond to CorCallingConvention CORINFO_CALLCONV_DEFAULT = 0x0, - // Instead of using the below values, use the CorInfoCallConvExtension enum for unmanaged calling conventions. - // CORINFO_CALLCONV_C = 0x1, - // CORINFO_CALLCONV_STDCALL = 0x2, - // CORINFO_CALLCONV_THISCALL = 0x3, - // CORINFO_CALLCONV_FASTCALL = 0x4, + CORINFO_CALLCONV_C = 0x1, + CORINFO_CALLCONV_STDCALL = 0x2, + CORINFO_CALLCONV_THISCALL = 0x3, + CORINFO_CALLCONV_FASTCALL = 0x4, CORINFO_CALLCONV_VARARG = 0x5, CORINFO_CALLCONV_FIELD = 0x6, CORINFO_CALLCONV_LOCAL_SIG = 0x7, @@ -688,29 +687,34 @@ enum CorInfoCallConv CORINFO_CALLCONV_PARAMTYPE = 0x80, // Passed last. Same as CORINFO_GENERICS_CTXT_FROM_PARAMTYPEARG }; -// Represents the calling conventions supported with the extensible calling convention syntax -// as well as the original metadata-encoded calling conventions. -enum class CorInfoCallConvExtension -{ - Managed, - C, - Stdcall, - Thiscall, - Fastcall - // New calling conventions supported with the extensible calling convention encoding go here. -}; - #ifdef UNIX_X86_ABI -inline bool IsCallerPop(CorInfoCallConvExtension callConv) +inline bool IsCallerPop(CorInfoCallConv callConv) { - return callConv == CorInfoCallConvExtension::Managed || callConv == CorInfoCallConvExtension::C; + unsigned int umask = CORINFO_CALLCONV_STDCALL + | CORINFO_CALLCONV_THISCALL + | CORINFO_CALLCONV_FASTCALL; + + return !(callConv & umask); } #endif // UNIX_X86_ABI +// Represents the calling conventions supported with the extensible calling convention syntax +// as well as the original metadata-encoded calling conventions. +enum CorInfoUnmanagedCallConv +{ + // These correspond to CorUnmanagedCallingConvention + CORINFO_UNMANAGED_CALLCONV_UNKNOWN, + CORINFO_UNMANAGED_CALLCONV_C, + CORINFO_UNMANAGED_CALLCONV_STDCALL, + CORINFO_UNMANAGED_CALLCONV_THISCALL, + CORINFO_UNMANAGED_CALLCONV_FASTCALL + // New calling conventions supported with the extensible calling convention encoding go here. +}; + // Determines whether or not this calling convention is an instance method calling convention. -inline bool callConvIsInstanceMethodCallConv(CorInfoCallConvExtension callConv) +inline bool callConvIsInstanceMethodCallConv(CorInfoUnmanagedCallConv callConv) { - return callConv == CorInfoCallConvExtension::Thiscall; + return callConv == CORINFO_UNMANAGED_CALLCONV_THISCALL; } // These are returned from getMethodOptions @@ -2076,14 +2080,9 @@ class ICorStaticInfo CORINFO_CLASS_HANDLE classHnd ) { return false; } - // return the entry point calling convention for any of the following - // - a P/Invoke - // - a method marked with UnmanagedCallersOnly - // - a function pointer with the CORINFO_CALLCONV_UNMANAGED calling convention. - virtual CorInfoCallConvExtension getUnmanagedCallConv( - CORINFO_METHOD_HANDLE method, - CORINFO_SIG_INFO* callSiteSig, - bool* pSuppressGCTransition /* OUT */ + // return the unmanaged calling convention for a PInvoke + virtual CorInfoUnmanagedCallConv getUnmanagedCallConv( + CORINFO_METHOD_HANDLE method ) = 0; // return if any marshaling is required for PInvoke methods. Note that diff --git a/src/coreclr/inc/icorjitinfoimpl_generated.h b/src/coreclr/inc/icorjitinfoimpl_generated.h index 7a07ae42f6f666..18bfd92a2c2fca 100644 --- a/src/coreclr/inc/icorjitinfoimpl_generated.h +++ b/src/coreclr/inc/icorjitinfoimpl_generated.h @@ -99,10 +99,8 @@ CorInfoIntrinsics getIntrinsicID( bool isIntrinsicType( CORINFO_CLASS_HANDLE classHnd) override; -CorInfoCallConvExtension getUnmanagedCallConv( - CORINFO_METHOD_HANDLE method, - CORINFO_SIG_INFO* callSiteSig, - bool* pSuppressGCTransition) override; +CorInfoUnmanagedCallConv getUnmanagedCallConv( + CORINFO_METHOD_HANDLE method) override; bool pInvokeMarshalingRequired( CORINFO_METHOD_HANDLE method, diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h index 26cb25e4a26349..5e3af43b8e5186 100644 --- a/src/coreclr/inc/jiteeversionguid.h +++ b/src/coreclr/inc/jiteeversionguid.h @@ -31,11 +31,11 @@ // ////////////////////////////////////////////////////////////////////////////////////////////////////////// -constexpr GUID JITEEVersionIdentifier = { /* a7bb194e-4e7c-4850-af12-ea9f30ea5a13 */ - 0xa7bb194e, - 0x4e7c, - 0x4850, - {0xaf, 0x12, 0xea, 0x9f, 0x30, 0xea, 0x5a, 0x13} +constexpr GUID JITEEVersionIdentifier = { /* 790de1e5-1426-4ecf-939c-2cd60445c219 */ + 0x790de1e5, + 0x1426, + 0x4ecf, + { 0x93, 0x9c, 0x2c, 0xd6, 0x4, 0x45, 0xc2, 0x19 } }; ////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/coreclr/jit/ICorJitInfo_API_wrapper.hpp b/src/coreclr/jit/ICorJitInfo_API_wrapper.hpp index 0788bc6af4e3ca..fb10358a4129f7 100644 --- a/src/coreclr/jit/ICorJitInfo_API_wrapper.hpp +++ b/src/coreclr/jit/ICorJitInfo_API_wrapper.hpp @@ -191,13 +191,11 @@ bool WrapICorJitInfo::isIntrinsicType( return temp; } -CorInfoCallConvExtension WrapICorJitInfo::getUnmanagedCallConv( - CORINFO_METHOD_HANDLE method, - CORINFO_SIG_INFO* callSiteSig, - bool* pSuppressGCTransition) +CorInfoUnmanagedCallConv WrapICorJitInfo::getUnmanagedCallConv( + CORINFO_METHOD_HANDLE method) { API_ENTER(getUnmanagedCallConv); - CorInfoCallConvExtension temp = wrapHnd->getUnmanagedCallConv(method, callSiteSig, pSuppressGCTransition); + CorInfoUnmanagedCallConv temp = wrapHnd->getUnmanagedCallConv(method); API_LEAVE(getUnmanagedCallConv); return temp; } diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index fb3065e24d0438..3613312f75fcb9 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -8933,7 +8933,7 @@ void CodeGen::genFnEpilog(BasicBlock* block) fCalleePop = false; #ifdef UNIX_X86_ABI - if (IsCallerPop(compiler->info.compCallConv)) + if (IsCallerPop(compiler->info.compMethodInfo->args.callConv)) fCalleePop = false; #endif // UNIX_X86_ABI @@ -11534,7 +11534,8 @@ void CodeGen::genReturn(GenTree* treeNode) } else // we must have a struct return type { - CorInfoCallConvExtension callConv = compiler->info.compCallConv; + CorInfoCallConvExtension callConv = + compiler->compMethodInfoGetEntrypointCallConv(compiler->info.compMethodInfo); retTypeDesc.InitializeStructReturnType(compiler, compiler->info.compMethodInfo->args.retTypeClass, callConv); @@ -11662,7 +11663,9 @@ void CodeGen::genStructReturn(GenTree* treeNode) if (actualOp1->OperIs(GT_LCL_VAR)) { varDsc = compiler->lvaGetDesc(actualOp1->AsLclVar()->GetLclNum()); - retTypeDesc.InitializeStructReturnType(compiler, varDsc->GetStructHnd(), compiler->info.compCallConv); + retTypeDesc.InitializeStructReturnType(compiler, varDsc->GetStructHnd(), + compiler->compMethodInfoGetEntrypointCallConv( + compiler->info.compMethodInfo)); assert(varDsc->lvIsMultiRegRet); } else @@ -11825,8 +11828,8 @@ void CodeGen::genMultiRegStoreToLocal(GenTreeLclVar* lclNode) } for (unsigned i = 0; i < regCount; ++i) { - regNumber reg = genConsumeReg(op1, i); - var_types srcType = actualOp1->GetRegTypeByIndex(i); + regNumber reg = genConsumeReg(op1, i); + var_types type = actualOp1->GetRegTypeByIndex(i); // genConsumeReg will return the valid register, either from the COPY // or from the original source. assert(reg != REG_NA); @@ -11836,14 +11839,14 @@ void CodeGen::genMultiRegStoreToLocal(GenTreeLclVar* lclNode) regNumber varReg = lclNode->GetRegByIndex(i); unsigned fieldLclNum = varDsc->lvFieldLclStart + i; LclVarDsc* fieldVarDsc = compiler->lvaGetDesc(fieldLclNum); + var_types type = fieldVarDsc->TypeGet(); if (varReg != REG_NA) { - var_types destType = fieldVarDsc->TypeGet(); - hasRegs = true; + hasRegs = true; if (varReg != reg) { // We may need a cross register-file copy here. - inst_RV_RV(ins_Copy(reg, destType), varReg, reg, destType); + inst_RV_RV(ins_Copy(reg, type), varReg, reg, type); } fieldVarDsc->SetRegNum(varReg); } @@ -11855,15 +11858,15 @@ void CodeGen::genMultiRegStoreToLocal(GenTreeLclVar* lclNode) { if (!lclNode->AsLclVar()->IsLastUse(i)) { - GetEmitter()->emitIns_S_R(ins_Store(srcType), emitTypeSize(srcType), reg, fieldLclNum, 0); + GetEmitter()->emitIns_S_R(ins_Store(type), emitTypeSize(type), reg, fieldLclNum, 0); } } fieldVarDsc->SetRegNum(varReg); } else { - GetEmitter()->emitIns_S_R(ins_Store(srcType), emitTypeSize(srcType), reg, lclNum, offset); - offset += genTypeSize(srcType); + GetEmitter()->emitIns_S_R(ins_Store(type), emitTypeSize(type), reg, lclNum, offset); + offset += genTypeSize(type); } } diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp index 648a11223c2e8d..6f1ba9ab9bb8b6 100644 --- a/src/coreclr/jit/codegenxarch.cpp +++ b/src/coreclr/jit/codegenxarch.cpp @@ -141,7 +141,8 @@ void CodeGen::genEmitGSCookieCheck(bool pushReg) else // we must have a struct return type { retTypeDesc.InitializeStructReturnType(compiler, compiler->info.compMethodInfo->args.retTypeClass, - compiler->info.compCallConv); + compiler->compMethodInfoGetEntrypointCallConv( + compiler->info.compMethodInfo)); } const unsigned regCount = retTypeDesc.GetReturnRegCount(); diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index 171bebb45e72b6..4642388b574e47 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -2055,6 +2055,22 @@ unsigned Compiler::compGetTypeSize(CorInfoType cit, CORINFO_CLASS_HANDLE clsHnd) return sigSize; } +CorInfoCallConvExtension Compiler::compMethodInfoGetEntrypointCallConv(CORINFO_METHOD_INFO* mthInfo) +{ + CorInfoCallConv callConv = mthInfo->args.getCallConv(); + if (callConv == CORINFO_CALLCONV_DEFAULT || callConv == CORINFO_CALLCONV_VARARG) + { + // Both the default and the varargs calling conventions represent a managed callconv. + return CorInfoCallConvExtension::Managed; + } + + static_assert_no_msg((unsigned)CorInfoCallConvExtension::C == (unsigned)CORINFO_CALLCONV_C); + static_assert_no_msg((unsigned)CorInfoCallConvExtension::Stdcall == (unsigned)CORINFO_CALLCONV_STDCALL); + static_assert_no_msg((unsigned)CorInfoCallConvExtension::Thiscall == (unsigned)CORINFO_CALLCONV_THISCALL); + + return (CorInfoCallConvExtension)callConv; +} + #ifdef DEBUG static bool DidComponentUnitTests = false; @@ -2570,7 +2586,6 @@ void Compiler::compInitOptions(JitFlags* jitFlags) assert(!jitFlags->IsSet(JitFlags::JIT_FLAG_PROF_ENTERLEAVE)); assert(!jitFlags->IsSet(JitFlags::JIT_FLAG_DEBUG_EnC)); assert(!jitFlags->IsSet(JitFlags::JIT_FLAG_DEBUG_INFO)); - assert(!jitFlags->IsSet(JitFlags::JIT_FLAG_REVERSE_PINVOKE)); } opts.jitFlags = jitFlags; @@ -6093,28 +6108,21 @@ int Compiler::compCompileHelper(CORINFO_MODULE_HANDLE classPtr, info.compHasNextCallRetAddr = false; - if (opts.IsReversePInvoke()) - { - bool unused; - info.compCallConv = info.compCompHnd->getUnmanagedCallConv(methodInfo->ftn, nullptr, &unused); - } - else - { - info.compCallConv = CorInfoCallConvExtension::Managed; - } - - info.compIsVarArgs = false; - switch (methodInfo->args.getCallConv()) { - case CORINFO_CALLCONV_NATIVEVARARG: case CORINFO_CALLCONV_VARARG: + case CORINFO_CALLCONV_NATIVEVARARG: info.compIsVarArgs = true; break; - default: + case CORINFO_CALLCONV_C: + case CORINFO_CALLCONV_STDCALL: + case CORINFO_CALLCONV_THISCALL: + case CORINFO_CALLCONV_DEFAULT: + info.compIsVarArgs = false; break; + default: + BADCODE("bad calling convention"); } - info.compRetNativeType = info.compRetType = JITtype2varType(methodInfo->args.retType); info.compUnmanagedCallCountWithGCTransition = 0; diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 67c6e0e582e0b7..aa4d63b23a3ed1 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -3849,7 +3849,7 @@ class Compiler CORINFO_CLASS_HANDLE impGetSpecialIntrinsicExactReturnType(CORINFO_METHOD_HANDLE specialIntrinsicHandle); - bool impMethodInfo_hasRetBuffArg(CORINFO_METHOD_INFO* methInfo, CorInfoCallConvExtension callConv); + bool impMethodInfo_hasRetBuffArg(CORINFO_METHOD_INFO* methInfo); GenTree* impFixupCallStructReturn(GenTreeCall* call, CORINFO_CLASS_HANDLE retClsHnd); @@ -9308,8 +9308,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX unsigned compUnmanagedCallCountWithGCTransition; // count of unmanaged calls with GC transition. - CorInfoCallConvExtension compCallConv; // The entry-point calling convention for this method. - unsigned compLvFrameListRoot; // lclNum for the Frame root unsigned compXcptnsCount; // Number of exception-handling clauses read in the method's IL. // You should generally use compHndBBtabCount instead: it is the @@ -9389,7 +9387,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX // to be returned in x0. CLANG_FORMAT_COMMENT_ANCHOR; #if defined(TARGET_WINDOWS) && defined(TARGET_ARM64) - auto callConv = info.compCallConv; + auto callConv = compMethodInfoGetEntrypointCallConv(info.compMethodInfo); if (callConvIsInstanceMethodCallConv(callConv)) { return (info.compRetBuffArg != BAD_VAR_NUM); @@ -9595,8 +9593,8 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX // size of the type these describe. unsigned compGetTypeSize(CorInfoType cit, CORINFO_CLASS_HANDLE clsHnd); - // Returns true if the method being compiled has a return buffer. - bool compHasRetBuffArg(); + // Gets the calling convention the method's entry point should have. + CorInfoCallConvExtension compMethodInfoGetEntrypointCallConv(CORINFO_METHOD_INFO* mthInfo); #ifdef DEBUG // Components used by the compiler may write unit test suites, and diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index 46c4e75cdfbc14..f22c4cde83f814 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -23497,7 +23497,6 @@ void Compiler::fgInvokeInlineeCompiler(GenTreeCall* call, InlineResult* inlineRe compileFlagsForInlinee.Clear(JitFlags::JIT_FLAG_PROF_ENTERLEAVE); compileFlagsForInlinee.Clear(JitFlags::JIT_FLAG_DEBUG_EnC); compileFlagsForInlinee.Clear(JitFlags::JIT_FLAG_DEBUG_INFO); - compileFlagsForInlinee.Clear(JitFlags::JIT_FLAG_REVERSE_PINVOKE); compileFlagsForInlinee.Set(JitFlags::JIT_FLAG_SKIP_VERIFICATION); diff --git a/src/coreclr/jit/gcencode.cpp b/src/coreclr/jit/gcencode.cpp index bfe9dbe3ee9188..b8228fe6ce024f 100644 --- a/src/coreclr/jit/gcencode.cpp +++ b/src/coreclr/jit/gcencode.cpp @@ -50,7 +50,9 @@ ReturnKind GCInfo::getReturnKind() case TYP_STRUCT: { CORINFO_CLASS_HANDLE structType = compiler->info.compMethodInfo->args.retTypeClass; - var_types retType = compiler->getReturnTypeForStruct(structType, compiler->info.compCallConv); + var_types retType = + compiler->getReturnTypeForStruct(structType, compiler->compMethodInfoGetEntrypointCallConv( + compiler->info.compMethodInfo)); switch (retType) { diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index ff6556dab12b6a..20fcdfa90e347b 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -1319,6 +1319,7 @@ GenTree* Compiler::impAssignStructPtr(GenTree* destAddr, // The argument list has already been reversed. // Insert the return buffer as the last node so it will be pushed on to the stack last // as required by the native ABI. + assert(srcCall->gtCallType == CT_INDIRECT); GenTreeCall::Use* lastArg = srcCall->gtCallArgs; if (lastArg == nullptr) { @@ -7023,7 +7024,7 @@ bool Compiler::impCanPInvokeInlineCallSite(BasicBlock* block) void Compiler::impCheckForPInvokeCall( GenTreeCall* call, CORINFO_METHOD_HANDLE methHnd, CORINFO_SIG_INFO* sig, unsigned mflags, BasicBlock* block) { - CorInfoCallConvExtension unmanagedCallConv; + CorInfoUnmanagedCallConv unmanagedCallConv; // If VM flagged it as Pinvoke, flag the call node accordingly if ((mflags & CORINFO_FLG_PINVOKE) != 0) @@ -7031,7 +7032,11 @@ void Compiler::impCheckForPInvokeCall( call->gtCallMoreFlags |= GTF_CALL_M_PINVOKE; } - bool suppressGCTransition = false; + if ((sig->flags & CORINFO_SIGFLAG_SUPPRESS_GC_TRANSITION) != 0) + { + call->gtCallMoreFlags |= GTF_CALL_M_SUPPRESS_GC_TRANSITION; + } + if (methHnd) { if ((mflags & CORINFO_FLG_PINVOKE) == 0) @@ -7039,27 +7044,26 @@ void Compiler::impCheckForPInvokeCall( return; } - unmanagedCallConv = info.compCompHnd->getUnmanagedCallConv(methHnd, nullptr, &suppressGCTransition); + unmanagedCallConv = info.compCompHnd->getUnmanagedCallConv(methHnd); } else { - if (sig->getCallConv() == CORINFO_CALLCONV_DEFAULT || sig->getCallConv() == CORINFO_CALLCONV_VARARG) + CorInfoCallConv callConv = CorInfoCallConv(sig->callConv & CORINFO_CALLCONV_MASK); + if (callConv == CORINFO_CALLCONV_NATIVEVARARG) { - return; + // Used by the IL Stubs. + callConv = CORINFO_CALLCONV_C; } - - unmanagedCallConv = info.compCompHnd->getUnmanagedCallConv(nullptr, sig, &suppressGCTransition); + static_assert_no_msg((unsigned)CORINFO_CALLCONV_C == (unsigned)CORINFO_UNMANAGED_CALLCONV_C); + static_assert_no_msg((unsigned)CORINFO_CALLCONV_STDCALL == (unsigned)CORINFO_UNMANAGED_CALLCONV_STDCALL); + static_assert_no_msg((unsigned)CORINFO_CALLCONV_THISCALL == (unsigned)CORINFO_UNMANAGED_CALLCONV_THISCALL); + unmanagedCallConv = CorInfoUnmanagedCallConv(callConv); assert(!call->gtCallCookie); } - if (suppressGCTransition) - { - call->gtCallMoreFlags |= GTF_CALL_M_SUPPRESS_GC_TRANSITION; - } - - if (unmanagedCallConv != CorInfoCallConvExtension::C && unmanagedCallConv != CorInfoCallConvExtension::Stdcall && - unmanagedCallConv != CorInfoCallConvExtension::Thiscall) + if (unmanagedCallConv != CORINFO_UNMANAGED_CALLCONV_C && unmanagedCallConv != CORINFO_UNMANAGED_CALLCONV_STDCALL && + unmanagedCallConv != CORINFO_UNMANAGED_CALLCONV_THISCALL) { return; } @@ -7115,20 +7119,24 @@ void Compiler::impCheckForPInvokeCall( JITLOG((LL_INFO1000000, "\nInline a CALLI PINVOKE call from method %s", info.compFullName)); + static_assert_no_msg((unsigned)CorInfoCallConvExtension::C == (unsigned)CORINFO_UNMANAGED_CALLCONV_C); + static_assert_no_msg((unsigned)CorInfoCallConvExtension::Stdcall == (unsigned)CORINFO_UNMANAGED_CALLCONV_STDCALL); + static_assert_no_msg((unsigned)CorInfoCallConvExtension::Thiscall == (unsigned)CORINFO_UNMANAGED_CALLCONV_THISCALL); + call->gtFlags |= GTF_CALL_UNMANAGED; - call->unmgdCallConv = unmanagedCallConv; + call->unmgdCallConv = CorInfoCallConvExtension(unmanagedCallConv); if (!call->IsSuppressGCTransition()) { info.compUnmanagedCallCountWithGCTransition++; } // AMD64 convention is same for native and managed - if (unmanagedCallConv == CorInfoCallConvExtension::C) + if (unmanagedCallConv == CORINFO_UNMANAGED_CALLCONV_C) { call->gtFlags |= GTF_CALL_POP_ARGS; } - if (unmanagedCallConv == CorInfoCallConvExtension::Thiscall) + if (unmanagedCallConv == CORINFO_UNMANAGED_CALLCONV_THISCALL) { call->gtCallMoreFlags |= GTF_CALL_M_UNMGD_THISCALL; } @@ -8401,6 +8409,12 @@ var_types Compiler::impImportCall(OPCODE opcode, } #endif // !FEATURE_VARARG +#ifdef UNIX_X86_ABI + // On Unix x86 we usually use caller-cleaned convention. + if (!call->AsCall()->IsUnmanaged() && IsCallerPop(sig->callConv)) + call->gtFlags |= GTF_CALL_POP_ARGS; +#endif // UNIX_X86_ABI + if ((sig->callConv & CORINFO_CALLCONV_MASK) == CORINFO_CALLCONV_VARARG || (sig->callConv & CORINFO_CALLCONV_MASK) == CORINFO_CALLCONV_NATIVEVARARG) { @@ -8476,12 +8490,6 @@ var_types Compiler::impImportCall(OPCODE opcode, impCheckForPInvokeCall(call->AsCall(), methHnd, sig, mflags, block); } -#ifdef UNIX_X86_ABI - // On Unix x86 we use caller-cleaned convention. - if ((call->gtFlags & GTF_CALL_UNMANAGED) == 0) - call->gtFlags |= GTF_CALL_POP_ARGS; -#endif // UNIX_X86_ABI - if (call->gtFlags & GTF_CALL_UNMANAGED) { // We set up the unmanaged call by linking the frame, disabling GC, etc @@ -8498,8 +8506,10 @@ 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)) + else if ((opcode == CEE_CALLI) && (((sig->callConv & CORINFO_CALLCONV_MASK) == CORINFO_CALLCONV_STDCALL) || + ((sig->callConv & CORINFO_CALLCONV_MASK) == CORINFO_CALLCONV_C) || + ((sig->callConv & CORINFO_CALLCONV_MASK) == CORINFO_CALLCONV_THISCALL) || + ((sig->callConv & CORINFO_CALLCONV_MASK) == CORINFO_CALLCONV_FASTCALL))) { if (!info.compCompHnd->canGetCookieForPInvokeCalliSig(sig)) { @@ -8901,8 +8911,9 @@ var_types Compiler::impImportCall(OPCODE opcode, // a small-typed return value is responsible for normalizing the return val if (canTailCall && - !impTailCallRetTypeCompatible(info.compRetType, info.compMethodInfo->args.retTypeClass, info.compCallConv, - callRetTyp, sig->retTypeClass, call->AsCall()->GetUnmanagedCallConv())) + !impTailCallRetTypeCompatible(info.compRetType, info.compMethodInfo->args.retTypeClass, + compMethodInfoGetEntrypointCallConv(info.compMethodInfo), callRetTyp, + sig->retTypeClass, call->AsCall()->GetUnmanagedCallConv())) { canTailCall = false; szCanTailCallFailReason = "Return types are not tail call compatible"; @@ -9245,7 +9256,7 @@ var_types Compiler::impImportCall(OPCODE opcode, #pragma warning(pop) #endif -bool Compiler::impMethodInfo_hasRetBuffArg(CORINFO_METHOD_INFO* methInfo, CorInfoCallConvExtension callConv) +bool Compiler::impMethodInfo_hasRetBuffArg(CORINFO_METHOD_INFO* methInfo) { CorInfoType corType = methInfo->args.retType; @@ -9254,7 +9265,9 @@ bool Compiler::impMethodInfo_hasRetBuffArg(CORINFO_METHOD_INFO* methInfo, CorInf // We have some kind of STRUCT being returned structPassingKind howToReturnStruct = SPK_Unknown; - var_types returnType = getReturnTypeForStruct(methInfo->args.retTypeClass, callConv, &howToReturnStruct); + var_types returnType = + getReturnTypeForStruct(methInfo->args.retTypeClass, compMethodInfoGetEntrypointCallConv(methInfo), + &howToReturnStruct); if (howToReturnStruct == SPK_ByReference) { @@ -16969,7 +16982,8 @@ bool Compiler::impReturnInstruction(int prefixFlags, OPCODE& opcode) noway_assert(info.compRetBuffArg == BAD_VAR_NUM); // adjust the type away from struct to integral // and no normalizing - op2 = impFixupStructReturnType(op2, retClsHnd, info.compCallConv); + op2 = impFixupStructReturnType(op2, retClsHnd, + compMethodInfoGetEntrypointCallConv(info.compMethodInfo)); } else { @@ -17169,7 +17183,8 @@ bool Compiler::impReturnInstruction(int prefixFlags, OPCODE& opcode) // Same as !IsHfa but just don't bother with impAssignStructPtr. #else // defined(UNIX_AMD64_ABI) ReturnTypeDesc retTypeDesc; - retTypeDesc.InitializeStructReturnType(this, retClsHnd, info.compCallConv); + retTypeDesc.InitializeStructReturnType(this, retClsHnd, + compMethodInfoGetEntrypointCallConv(info.compMethodInfo)); unsigned retRegCount = retTypeDesc.GetReturnRegCount(); if (retRegCount != 0) @@ -17203,7 +17218,8 @@ bool Compiler::impReturnInstruction(int prefixFlags, OPCODE& opcode) else #elif defined(TARGET_ARM64) ReturnTypeDesc retTypeDesc; - retTypeDesc.InitializeStructReturnType(this, retClsHnd, info.compCallConv); + retTypeDesc.InitializeStructReturnType(this, retClsHnd, + compMethodInfoGetEntrypointCallConv(info.compMethodInfo)); unsigned retRegCount = retTypeDesc.GetReturnRegCount(); if (retRegCount != 0) @@ -17227,7 +17243,8 @@ bool Compiler::impReturnInstruction(int prefixFlags, OPCODE& opcode) else #elif defined(TARGET_X86) ReturnTypeDesc retTypeDesc; - retTypeDesc.InitializeStructReturnType(this, retClsHnd, info.compCallConv); + retTypeDesc.InitializeStructReturnType(this, retClsHnd, + compMethodInfoGetEntrypointCallConv(info.compMethodInfo)); unsigned retRegCount = retTypeDesc.GetReturnRegCount(); if (retRegCount != 0) @@ -17316,7 +17333,7 @@ bool Compiler::impReturnInstruction(int prefixFlags, OPCODE& opcode) #if defined(TARGET_WINDOWS) && defined(TARGET_ARM64) // On ARM64, the native instance calling convention variant // requires the implicit ByRef to be explicitly returned. - else if (callConvIsInstanceMethodCallConv(info.compCallConv)) + else if (callConvIsInstanceMethodCallConv(compMethodInfoGetEntrypointCallConv(info.compMethodInfo))) { op1 = gtNewOperNode(GT_RETURN, TYP_BYREF, gtNewLclvNode(info.compRetBuffArg, TYP_BYREF)); } @@ -17335,7 +17352,7 @@ bool Compiler::impReturnInstruction(int prefixFlags, OPCODE& opcode) // Also on System V AMD64 the multireg structs returns are also left as structs. noway_assert(info.compRetNativeType != TYP_STRUCT); #endif - op2 = impFixupStructReturnType(op2, retClsHnd, info.compCallConv); + op2 = impFixupStructReturnType(op2, retClsHnd, compMethodInfoGetEntrypointCallConv(info.compMethodInfo)); // return op2 var_types returnType; if (compDoOldStructRetyping()) @@ -19454,8 +19471,7 @@ void Compiler::impInlineInitVars(InlineInfo* pInlineInfo) InlLclVarInfo* lclVarInfo = pInlineInfo->lclVarInfo; InlineResult* inlineResult = pInlineInfo->inlineResult; - // Inlined methods always use the managed calling convention - const bool hasRetBuffArg = impMethodInfo_hasRetBuffArg(methInfo, CorInfoCallConvExtension::Managed); + const bool hasRetBuffArg = impMethodInfo_hasRetBuffArg(methInfo); /* init the argument stuct */ diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index 49a2879a4a2bdf..4d8230187e284c 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -135,7 +135,7 @@ void Compiler::lvaInitTypeRef() // Are we returning a struct using a return buffer argument? // - const bool hasRetBuffArg = impMethodInfo_hasRetBuffArg(info.compMethodInfo, info.compCallConv); + const bool hasRetBuffArg = impMethodInfo_hasRetBuffArg(info.compMethodInfo); // Possibly change the compRetNativeType from TYP_STRUCT to a "primitive" type // when we are returning a struct by value and it fits in one register @@ -145,7 +145,9 @@ void Compiler::lvaInitTypeRef() CORINFO_CLASS_HANDLE retClsHnd = info.compMethodInfo->args.retTypeClass; Compiler::structPassingKind howToReturnStruct; - var_types returnType = getReturnTypeForStruct(retClsHnd, info.compCallConv, &howToReturnStruct); + var_types returnType = + getReturnTypeForStruct(retClsHnd, compMethodInfoGetEntrypointCallConv(info.compMethodInfo), + &howToReturnStruct); // We can safely widen the return type for enclosed structs. if ((howToReturnStruct == SPK_PrimitiveType) || (howToReturnStruct == SPK_EnclosingType)) @@ -351,7 +353,7 @@ void Compiler::lvaInitArgs(InitVarDscInfo* varDscInfo) unsigned numUserArgsToSkip = 0; unsigned numUserArgs = info.compMethodInfo->args.numArgs; #if defined(TARGET_WINDOWS) && !defined(TARGET_ARM) - if (callConvIsInstanceMethodCallConv(info.compCallConv)) + if (callConvIsInstanceMethodCallConv(compMethodInfoGetEntrypointCallConv(info.compMethodInfo))) { // If we are a native instance method, handle the first user arg // (the unmanaged this parameter) and then handle the hidden @@ -503,7 +505,7 @@ void Compiler::lvaInitThisPtr(InitVarDscInfo* varDscInfo) void Compiler::lvaInitRetBuffArg(InitVarDscInfo* varDscInfo, bool useFixedRetBufReg) { LclVarDsc* varDsc = varDscInfo->varDsc; - bool hasRetBuffArg = impMethodInfo_hasRetBuffArg(info.compMethodInfo, info.compCallConv); + bool hasRetBuffArg = impMethodInfo_hasRetBuffArg(info.compMethodInfo); // These two should always match noway_assert(hasRetBuffArg == varDscInfo->hasRetBufArg); @@ -5347,7 +5349,7 @@ void Compiler::lvaAssignVirtualFrameOffsetsToArgs() // the this parameter comes before the hidden return buffer parameter. // So, we want to process the native "this" parameter before we process // the native return buffer parameter. - if (callConvIsInstanceMethodCallConv(info.compCallConv)) + if (callConvIsInstanceMethodCallConv(compMethodInfoGetEntrypointCallConv(info.compMethodInfo))) { noway_assert(lvaTable[lclNum].lvIsRegArg); #ifndef TARGET_X86 diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index d7488c85228757..176eac0d15a990 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -3016,7 +3016,9 @@ void Lowering::LowerRet(GenTreeUnOp* ret) ReturnTypeDesc retTypeDesc; LclVarDsc* varDsc = nullptr; varDsc = comp->lvaGetDesc(retVal->AsLclVar()->GetLclNum()); - retTypeDesc.InitializeStructReturnType(comp, varDsc->GetStructHnd(), comp->info.compCallConv); + retTypeDesc.InitializeStructReturnType(comp, varDsc->GetStructHnd(), + comp->compMethodInfoGetEntrypointCallConv( + comp->info.compMethodInfo)); if (retTypeDesc.GetReturnRegCount() > 1) { CheckMultiRegLclVar(retVal->AsLclVar(), &retTypeDesc); diff --git a/src/coreclr/jit/lsrabuild.cpp b/src/coreclr/jit/lsrabuild.cpp index ac383e51231859..a0cbf2d5f24a9d 100644 --- a/src/coreclr/jit/lsrabuild.cpp +++ b/src/coreclr/jit/lsrabuild.cpp @@ -3507,7 +3507,8 @@ int LinearScan::BuildReturn(GenTree* tree) LclVarDsc* varDsc = compiler->lvaGetDesc(op1->AsLclVar()->GetLclNum()); ReturnTypeDesc retTypeDesc; retTypeDesc.InitializeStructReturnType(compiler, varDsc->GetStructHnd(), - compiler->info.compCallConv); + compiler->compMethodInfoGetEntrypointCallConv( + compiler->info.compMethodInfo)); pRetTypeDesc = &retTypeDesc; assert(compiler->lvaGetDesc(op1->AsLclVar()->GetLclNum())->lvFieldCnt == retTypeDesc.GetReturnRegCount()); diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 3f167b19dd7393..3b7b08ae65b2e1 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -6831,7 +6831,8 @@ bool Compiler::fgCanFastTailCall(GenTreeCall* callee, const char** failReason) if (callee->IsTailPrefixedCall()) { var_types retType = (compDoOldStructRetyping() ? info.compRetNativeType : info.compRetType); - assert(impTailCallRetTypeCompatible(retType, info.compMethodInfo->args.retTypeClass, info.compCallConv, + assert(impTailCallRetTypeCompatible(retType, info.compMethodInfo->args.retTypeClass, + compMethodInfoGetEntrypointCallConv(info.compMethodInfo), (var_types)callee->gtReturnType, callee->gtRetClsHnd, callee->GetUnmanagedCallConv())); } diff --git a/src/coreclr/jit/target.h b/src/coreclr/jit/target.h index 633f5dc34d22b9..5d3e7ad96d14c3 100644 --- a/src/coreclr/jit/target.h +++ b/src/coreclr/jit/target.h @@ -2027,6 +2027,24 @@ typedef target_ssize_t cnsval_ssize_t; typedef target_size_t cnsval_size_t; #endif +// Represents the calling conventions supported with the extensible calling convention syntax +// as well as the original metadata-encoded calling conventions. +enum class CorInfoCallConvExtension +{ + Managed, + C, + Stdcall, + Thiscall, + Fastcall + // New calling conventions supported with the extensible calling convention encoding go here. +}; + +// Determines whether or not this calling convention is an instance method calling convention. +inline bool callConvIsInstanceMethodCallConv(CorInfoCallConvExtension callConv) +{ + return callConv == CorInfoCallConvExtension::Thiscall; +} + /*****************************************************************************/ #endif // TARGET_H_ /*****************************************************************************/ diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoBase.cs b/src/coreclr/tools/Common/JitInterface/CorInfoBase.cs index 5808db5f534f74..2326320e34eb43 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoBase.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoBase.cs @@ -276,12 +276,12 @@ static byte _isIntrinsicType(IntPtr thisHandle, IntPtr* ppException, CORINFO_CLA } [UnmanagedCallersOnly] - static CorInfoCallConvExtension _getUnmanagedCallConv(IntPtr thisHandle, IntPtr* ppException, CORINFO_METHOD_STRUCT_* method, CORINFO_SIG_INFO* callSiteSig, bool* pSuppressGCTransition) + static CorInfoUnmanagedCallConv _getUnmanagedCallConv(IntPtr thisHandle, IntPtr* ppException, CORINFO_METHOD_STRUCT_* method) { var _this = GetThis(thisHandle); try { - return _this.getUnmanagedCallConv(method, callSiteSig, ref *pSuppressGCTransition); + return _this.getUnmanagedCallConv(method); } catch (Exception ex) { @@ -2556,7 +2556,7 @@ static IntPtr GetUnmanagedCallbacks() callbacks[15] = (delegate* unmanaged)&_expandRawHandleIntrinsic; callbacks[16] = (delegate* unmanaged)&_getIntrinsicID; callbacks[17] = (delegate* unmanaged)&_isIntrinsicType; - callbacks[18] = (delegate* unmanaged)&_getUnmanagedCallConv; + callbacks[18] = (delegate* unmanaged)&_getUnmanagedCallConv; callbacks[19] = (delegate* unmanaged)&_pInvokeMarshalingRequired; callbacks[20] = (delegate* unmanaged)&_satisfiesMethodConstraints; callbacks[21] = (delegate* unmanaged)&_isCompatibleDelegate; diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index c88f858afacfd2..b31079430320be 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -3,11 +3,9 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Diagnostics; using System.IO; using System.Text; -using System.Reflection.Metadata; using System.Runtime.CompilerServices; using System.Runtime.ExceptionServices; using System.Runtime.InteropServices; @@ -476,7 +474,7 @@ private bool Get_CORINFO_METHOD_INFO(MethodDesc method, MethodIL methodIL, CORIN methodInfo->options |= CorInfoOptions.CORINFO_GENERICS_CTXT_FROM_METHODTABLE; } methodInfo->regionKind = CorInfoRegionKind.CORINFO_REGION_NONE; - Get_CORINFO_SIG_INFO(method, sig: &methodInfo->args); + Get_CORINFO_SIG_INFO(method, &methodInfo->args); Get_CORINFO_SIG_INFO(methodIL.GetLocals(), &methodInfo->locals); return true; @@ -486,6 +484,11 @@ private void Get_CORINFO_SIG_INFO(MethodDesc method, CORINFO_SIG_INFO* sig, bool { Get_CORINFO_SIG_INFO(method.Signature, sig); + if (method.IsPInvoke && method.IsSuppressGCTransition()) + { + sig->flags |= CorInfoSigInfoFlags.CORINFO_SIGFLAG_SUPPRESS_GC_TRANSITION; + } + // Does the method have a hidden parameter? bool hasHiddenParameter = !suppressHiddenArgument && method.RequiresInstArg(); @@ -519,52 +522,9 @@ private void Get_CORINFO_SIG_INFO(MethodDesc method, CORINFO_SIG_INFO* sig, bool } } - private CorInfoCallConvExtension GetUnmanagedCallingConventionFromAttribute(CustomAttributeValue unmanagedCallersOnlyAttribute) - { - CorInfoCallConvExtension callConv = (CorInfoCallConvExtension)PlatformDefaultUnmanagedCallingConvention(); - - ImmutableArray> callConvArray = default; - foreach (var arg in unmanagedCallersOnlyAttribute.NamedArguments) - { - if (arg.Name == "CallConvs") - { - callConvArray = (ImmutableArray>)arg.Value; - } - } - - // No calling convention was specified in the attribute, so return the default. - if (callConvArray.IsDefault) - { - return callConv; - } - - bool found = false; - foreach (CustomAttributeTypedArgument type in callConvArray) - { - if (!(type.Value is DefType defType)) - continue; - - if (defType.Namespace != "System.Runtime.CompilerServices") - continue; - - CorInfoCallConvExtension? callConvLocal = GetCallingConventionForCallConvType(defType); - - if (callConvLocal.HasValue) - { - // Error if there are multiple recognized calling conventions - if (found) - ThrowHelper.ThrowInvalidProgramException(ExceptionStringID.InvalidProgramMultipleCallConv, MethodBeingCompiled); - - callConv = callConvLocal.Value; - found = true; - } - } - return callConv; - } - - private bool TryGetUnmanagedCallingConventionFromModOpt(MethodSignature signature, out CorInfoCallConvExtension callConv) + private bool TryGetUnmanagedCallingConventionFromModOpt(MethodSignature signature, out CorInfoCallConv callConv) { - callConv = CorInfoCallConvExtension.Managed; + callConv = CorInfoCallConv.CORINFO_CALLCONV_UNMANAGED; if (!signature.HasEmbeddedSignatureData || signature.GetEmbeddedSignatureData() == null) return false; @@ -585,14 +545,22 @@ private bool TryGetUnmanagedCallingConventionFromModOpt(MethodSignature signatur if (defType.Namespace != "System.Runtime.CompilerServices") continue; - CorInfoCallConvExtension? callConvLocal = GetCallingConventionForCallConvType(defType); + // Look for a recognized calling convention in metadata. + CorInfoCallConv? callConvLocal = defType.Name switch + { + "CallConvCdecl" => CorInfoCallConv.CORINFO_CALLCONV_C, + "CallConvStdcall" => CorInfoCallConv.CORINFO_CALLCONV_STDCALL, + "CallConvFastcall" => CorInfoCallConv.CORINFO_CALLCONV_FASTCALL, + "CallConvThiscall" => CorInfoCallConv.CORINFO_CALLCONV_THISCALL, + _ => null + }; if (callConvLocal.HasValue) { // Error if there are multiple recognized calling conventions if (found) ThrowHelper.ThrowInvalidProgramException(ExceptionStringID.InvalidProgramMultipleCallConv, MethodBeingCompiled); - + callConv = callConvLocal.Value; found = true; } @@ -601,17 +569,6 @@ private bool TryGetUnmanagedCallingConventionFromModOpt(MethodSignature signatur return found; } - private static CorInfoCallConvExtension? GetCallingConventionForCallConvType(DefType defType) => - // Look for a recognized calling convention in metadata. - defType.Name switch - { - "CallConvCdecl" => CorInfoCallConvExtension.C, - "CallConvStdcall" => CorInfoCallConvExtension.Stdcall, - "CallConvFastcall" => CorInfoCallConvExtension.Fastcall, - "CallConvThiscall" => CorInfoCallConvExtension.Thiscall, - _ => null - }; - private void Get_CORINFO_SIG_INFO(MethodSignature signature, CORINFO_SIG_INFO* sig) { sig->callConv = (CorInfoCallConv)(signature.Flags & MethodSignatureFlags.UnmanagedCallingConventionMask); @@ -622,6 +579,19 @@ private void Get_CORINFO_SIG_INFO(MethodSignature signature, CORINFO_SIG_INFO* s if (!signature.IsStatic) sig->callConv |= CorInfoCallConv.CORINFO_CALLCONV_HASTHIS; + // Unmanaged calling convention indicates modopt should be read + if (sig->callConv == CorInfoCallConv.CORINFO_CALLCONV_UNMANAGED) + { + if (TryGetUnmanagedCallingConventionFromModOpt(signature, out CorInfoCallConv callConvMaybe)) + { + sig->callConv = callConvMaybe; + } + else + { + sig->callConv = (CorInfoCallConv)PlatformDefaultUnmanagedCallingConvention(); + } + } + TypeDesc returnType = signature.ReturnType; CorInfoType corInfoRetType = asCorInfoType(signature.ReturnType, &sig->retTypeClass); @@ -641,7 +611,7 @@ private void Get_CORINFO_SIG_INFO(MethodSignature signature, CORINFO_SIG_INFO* s sig->pSig = (byte*)ObjectToHandle(signature); sig->cbSig = 0; // Not used by the JIT - sig->scope = null; + sig->scope = null; // Not used by the JIT sig->token = 0; // Not used by the JIT } @@ -694,7 +664,7 @@ private CorInfoType asCorInfoType(TypeDesc type, out TypeDesc typeIfNotPrimitive typeIfNotPrimitive = null; return CorInfoType.CORINFO_TYPE_PTR; } - + typeIfNotPrimitive = type; if (type.IsByRef) @@ -907,7 +877,7 @@ private void getMethodSig(CORINFO_METHOD_STRUCT_* ftn, CORINFO_SIG_INFO* sig, CO } } - Get_CORINFO_SIG_INFO(method, sig: sig); + Get_CORINFO_SIG_INFO(method, sig); } private bool getMethodInfo(CORINFO_METHOD_STRUCT_* ftn, CORINFO_METHOD_INFO* info) @@ -1055,86 +1025,19 @@ private MethodSignatureFlags PlatformDefaultUnmanagedCallingConvention() MethodSignatureFlags.UnmanagedCallingConventionStdCall : MethodSignatureFlags.UnmanagedCallingConventionCdecl; } - private CorInfoCallConvExtension getUnmanagedCallConv(CORINFO_METHOD_STRUCT_* method, CORINFO_SIG_INFO* sig, ref bool pSuppressGCTransition) + private CorInfoUnmanagedCallConv getUnmanagedCallConv(CORINFO_METHOD_STRUCT_* method) { - pSuppressGCTransition = false; - - if (method != null) - { - MethodDesc methodDesc = HandleToObject(method); - CorInfoCallConvExtension callConv = GetUnmanagedCallConv(HandleToObject(method), out pSuppressGCTransition); - return callConv; - } - else - { - Debug.Assert(sig != null); + MethodSignatureFlags unmanagedCallConv = HandleToObject(method).GetPInvokeMethodMetadata().Flags.UnmanagedCallingConvention; - CorInfoCallConvExtension callConv = GetUnmanagedCallConv((MethodSignature)HandleToObject((IntPtr)sig->pSig), out pSuppressGCTransition); - if (sig->flags.HasFlag(CorInfoSigInfoFlags.CORINFO_SIGFLAG_SUPPRESS_GC_TRANSITION)) - { - pSuppressGCTransition = true; - } - return callConv; - } - } - private CorInfoCallConvExtension GetUnmanagedCallConv(MethodDesc methodDesc, out bool suppressGCTransition) - { - suppressGCTransition = false; - MethodSignatureFlags callConv = methodDesc.Signature.Flags & MethodSignatureFlags.UnmanagedCallingConventionMask; - if (callConv == MethodSignatureFlags.None) - { - if (methodDesc.IsPInvoke) - { - suppressGCTransition = methodDesc.IsSuppressGCTransition(); - MethodSignatureFlags unmanagedCallConv = methodDesc.GetPInvokeMethodMetadata().Flags.UnmanagedCallingConvention; + if (unmanagedCallConv == MethodSignatureFlags.None) + unmanagedCallConv = PlatformDefaultUnmanagedCallingConvention(); - if (unmanagedCallConv == MethodSignatureFlags.None) - unmanagedCallConv = PlatformDefaultUnmanagedCallingConvention(); + // Verify that it is safe to convert MethodSignatureFlags.UnmanagedCallingConvention to CorInfoUnmanagedCallConv via a simple cast + Debug.Assert((int)CorInfoUnmanagedCallConv.CORINFO_UNMANAGED_CALLCONV_C == (int)MethodSignatureFlags.UnmanagedCallingConventionCdecl); + Debug.Assert((int)CorInfoUnmanagedCallConv.CORINFO_UNMANAGED_CALLCONV_STDCALL == (int)MethodSignatureFlags.UnmanagedCallingConventionStdCall); + Debug.Assert((int)CorInfoUnmanagedCallConv.CORINFO_UNMANAGED_CALLCONV_THISCALL == (int)MethodSignatureFlags.UnmanagedCallingConventionThisCall); - // Verify that it is safe to convert MethodSignatureFlags.UnmanagedCallingConvention to CorInfoCallConvExtension via a simple cast - Debug.Assert((int)CorInfoCallConvExtension.C == (int)MethodSignatureFlags.UnmanagedCallingConventionCdecl); - Debug.Assert((int)CorInfoCallConvExtension.Stdcall == (int)MethodSignatureFlags.UnmanagedCallingConventionStdCall); - Debug.Assert((int)CorInfoCallConvExtension.Thiscall == (int)MethodSignatureFlags.UnmanagedCallingConventionThisCall); - - return (CorInfoCallConvExtension)unmanagedCallConv; - } - else - { - Debug.Assert(methodDesc.IsUnmanagedCallersOnly); - CustomAttributeValue unmanagedCallersOnlyAttribute = ((EcmaMethod)methodDesc).GetDecodedCustomAttribute("System.Runtime.InteropServices", "UnmanagedCallersOnlyAttribute").Value; - return GetUnmanagedCallingConventionFromAttribute(unmanagedCallersOnlyAttribute); - } - } - return GetUnmanagedCallConv(methodDesc.Signature, out suppressGCTransition); - } - - private CorInfoCallConvExtension GetUnmanagedCallConv(MethodSignature signature, out bool suppressGCTransition) - { - suppressGCTransition = false; - switch (signature.Flags & MethodSignatureFlags.UnmanagedCallingConventionMask) - { - case MethodSignatureFlags.None: - ThrowHelper.ThrowInvalidProgramException(); - return CorInfoCallConvExtension.Managed; - case MethodSignatureFlags.UnmanagedCallingConventionCdecl: - return CorInfoCallConvExtension.C; - case MethodSignatureFlags.UnmanagedCallingConventionStdCall: - return CorInfoCallConvExtension.Stdcall; - case MethodSignatureFlags.UnmanagedCallingConventionThisCall: - return CorInfoCallConvExtension.Thiscall; - case MethodSignatureFlags.UnmanagedCallingConvention: - if (TryGetUnmanagedCallingConventionFromModOpt(signature, out CorInfoCallConvExtension callConvMaybe)) - { - return callConvMaybe; - } - else - { - return (CorInfoCallConvExtension)PlatformDefaultUnmanagedCallingConvention(); - } - default: - ThrowHelper.ThrowInvalidProgramException(); - return CorInfoCallConvExtension.Managed; - } + return (CorInfoUnmanagedCallConv)unmanagedCallConv; } private bool satisfiesMethodConstraints(CORINFO_CLASS_STRUCT_* parent, CORINFO_METHOD_STRUCT_* method) @@ -1384,6 +1287,9 @@ private void findSig(CORINFO_MODULE_STRUCT_* module, uint sigTOK, CORINFO_CONTEX Get_CORINFO_SIG_INFO(methodSig, sig); + // CORINFO_CALLCONV_UNMANAGED is handled by Get_CORINFO_SIG_INFO + Debug.Assert(sig->callConv != CorInfoCallConv.CORINFO_CALLCONV_UNMANAGED); + // TODO: Replace this with a public mechanism to mark calli with SuppressGCTransition once it becomes available. if (methodIL is PInvokeILStubMethodIL stubIL) { @@ -1408,7 +1314,7 @@ private void findSig(CORINFO_MODULE_STRUCT_* module, uint sigTOK, CORINFO_CONTEX private void findCallSiteSig(CORINFO_MODULE_STRUCT_* module, uint methTOK, CORINFO_CONTEXT_STRUCT* context, CORINFO_SIG_INFO* sig) { var methodIL = (MethodIL)HandleToObject((IntPtr)module); - Get_CORINFO_SIG_INFO(((MethodDesc)methodIL.GetObject((int)methTOK)), sig: sig); + Get_CORINFO_SIG_INFO(((MethodDesc)methodIL.GetObject((int)methTOK)), sig); } private CORINFO_CLASS_STRUCT_* getTokenTypeAsHandle(ref CORINFO_RESOLVED_TOKEN pResolvedToken) @@ -1739,7 +1645,7 @@ static bool ShouldAlign8(DefType type) { if (field.IsStatic) continue; - + instanceFields++; if (field.FieldType == doubleType) @@ -3092,7 +2998,7 @@ private ref ArrayBuilder findRelocBlock(BlockType blockType, out int length = _roData.Length; return ref _roDataRelocs; default: - throw new NotImplementedException("Arbitrary relocs"); + throw new NotImplementedException("Arbitrary relocs"); } } diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index 66b04f6996b947..d6eab1344394b8 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -63,7 +63,7 @@ public struct CORINFO_JUST_MY_CODE_HANDLE_ public struct CORINFO_VarArgInfo { } - + public struct PatchpointInfo { } @@ -314,11 +314,10 @@ public enum CorInfoCallConv // These correspond to CorCallingConvention CORINFO_CALLCONV_DEFAULT = 0x0, - // Instead of using the below values, use the CorInfoCallConvExtension enum for unmanaged calling conventions. - // CORINFO_CALLCONV_C = 0x1, - // CORINFO_CALLCONV_STDCALL = 0x2, - // CORINFO_CALLCONV_THISCALL = 0x3, - // CORINFO_CALLCONV_FASTCALL = 0x4, + CORINFO_CALLCONV_C = 0x1, + CORINFO_CALLCONV_STDCALL = 0x2, + CORINFO_CALLCONV_THISCALL = 0x3, + CORINFO_CALLCONV_FASTCALL = 0x4, CORINFO_CALLCONV_VARARG = 0x5, CORINFO_CALLCONV_FIELD = 0x6, CORINFO_CALLCONV_LOCAL_SIG = 0x7, @@ -333,16 +332,15 @@ public enum CorInfoCallConv CORINFO_CALLCONV_PARAMTYPE = 0x80, // Passed last. Same as CORINFO_GENERICS_CTXT_FROM_PARAMTYPEARG } - // Represents the calling conventions supported with the extensible calling convention syntax - // as well as the original metadata-encoded calling conventions. - enum CorInfoCallConvExtension + public enum CorInfoUnmanagedCallConv { - Managed, - C, - Stdcall, - Thiscall, - Fastcall - // New calling conventions supported with the extensible calling convention encoding go here. + // These correspond to CorUnmanagedCallingConvention + + CORINFO_UNMANAGED_CALLCONV_UNKNOWN, + CORINFO_UNMANAGED_CALLCONV_C, + CORINFO_UNMANAGED_CALLCONV_STDCALL, + CORINFO_UNMANAGED_CALLCONV_THISCALL, + CORINFO_UNMANAGED_CALLCONV_FASTCALL } public enum CORINFO_CALLINFO_FLAGS diff --git a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt index 46a18235a7cc93..f83c4924ca2408 100644 --- a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt +++ b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt @@ -1,18 +1,18 @@ ; Licensed to the .NET Foundation under one or more agreements. ; The .NET Foundation licenses this file to you under the MIT license. ; -; Thunk generator input file for generating the thunks from the C++ version of the -; jit interface to COM, into managed, and from COM to C++. +; Thunk generator input file for generating the thunks from the C++ version of the +; jit interface to COM, into managed, and from COM to C++. ; -; The format of this file is as follows. +; The format of this file is as follows. ; There are NORMALTYPES, RETURNTYPES, and FUNCTIONS regions -; In the NORMALTYPES/RETURNTYPES region, each type is described. If a type is +; In the NORMALTYPES/RETURNTYPES region, each type is described. If a type is ; described in the NORMALTYPES section, but isn't described in the RETURNTYPES section ; then the NORMALTYPES description can be used for a return value. ; ; TYPES have three fields ; ThunkDescriptionType,ManagedType,NativeType,NativeType2 -; If either ManagedType, NativeType, or NativeType2 are missing, then that form is replaced with ThunkDescriptionType. +; If either ManagedType, NativeType, or NativeType2 are missing, then that form is replaced with ThunkDescriptionType. ; NativeType is used for representing the type in headers which do not include the PAL and corinfo.h ; NativeType2 is used for representing the type in headers that are fully integrated with the PAL ; This feature allows reduction in type for enums and other types where the same type can be used in managed an native @@ -24,12 +24,12 @@ ; PointerToIntPointer,int**,int** ; ; Following the TYPES sections, there is the FUNCTIONS section -; Each function that is to be part of the interface is written here. The format is basically the C++ format +; Each function that is to be part of the interface is written here. The format is basically the C++ format ; without support for inline comments or sal annotations. ; ; Also, note that an empty line is ignored, and a line that begins with a ; is ignored. ; -; If the boilerplate around the individual functions needs adjustment, edit the thunk generator source code, and +; If the boilerplate around the individual functions needs adjustment, edit the thunk generator source code, and ; rebuild with rebuildthunkgen.cmd in the the ThunkGenerator subdir, then rebuildthunks.cmd ; If this file is editted, rebuild with gen.cmd -- DO NOT RUN from within a razzle window. ; @@ -123,7 +123,7 @@ CorInfoTailCall,,int CorInfoType,,int CorInfoHFAElemType,,int CorInfoTypeWithMod,,int -CorInfoCallConvExtension,,int +CorInfoUnmanagedCallConv,,int InfoAccessType,,int CORINFO_LOOKUP_KIND CORINFO_ACCESS_FLAGS,,int @@ -176,7 +176,7 @@ FUNCTIONS void expandRawHandleIntrinsic(CORINFO_RESOLVED_TOKEN * pResolvedToken, CORINFO_GENERICHANDLE_RESULT * pResult); CorInfoIntrinsics getIntrinsicID( CORINFO_METHOD_HANDLE method , BoolStar pMustExpand); bool isIntrinsicType( CORINFO_CLASS_HANDLE classHnd ); - CorInfoCallConvExtension getUnmanagedCallConv( CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* callSiteSig, bool* pSuppressGCTransition); + CorInfoUnmanagedCallConv getUnmanagedCallConv( CORINFO_METHOD_HANDLE method ); bool pInvokeMarshalingRequired( CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* callSiteSig ); bool satisfiesMethodConstraints( CORINFO_CLASS_HANDLE parent, CORINFO_METHOD_HANDLE method ); bool isCompatibleDelegate( CORINFO_CLASS_HANDLE objCls, CORINFO_CLASS_HANDLE methodParentCls, CORINFO_METHOD_HANDLE method, CORINFO_CLASS_HANDLE delegateCls, bool *pfIsOpenDelegate ); diff --git a/src/coreclr/tools/aot/jitinterface/jitinterface.h b/src/coreclr/tools/aot/jitinterface/jitinterface.h index 1d94c382277e53..e7a9609b6e4565 100644 --- a/src/coreclr/tools/aot/jitinterface/jitinterface.h +++ b/src/coreclr/tools/aot/jitinterface/jitinterface.h @@ -28,7 +28,7 @@ struct JitInterfaceCallbacks void (* expandRawHandleIntrinsic)(void * thisHandle, CorInfoExceptionClass** ppException, void* pResolvedToken, void* pResult); int (* getIntrinsicID)(void * thisHandle, CorInfoExceptionClass** ppException, void* method, bool* pMustExpand); bool (* isIntrinsicType)(void * thisHandle, CorInfoExceptionClass** ppException, void* classHnd); - int (* getUnmanagedCallConv)(void * thisHandle, CorInfoExceptionClass** ppException, void* method, void* callSiteSig, bool* pSuppressGCTransition); + int (* getUnmanagedCallConv)(void * thisHandle, CorInfoExceptionClass** ppException, void* method); bool (* pInvokeMarshalingRequired)(void * thisHandle, CorInfoExceptionClass** ppException, void* method, void* callSiteSig); bool (* satisfiesMethodConstraints)(void * thisHandle, CorInfoExceptionClass** ppException, void* parent, void* method); bool (* isCompatibleDelegate)(void * thisHandle, CorInfoExceptionClass** ppException, void* objCls, void* methodParentCls, void* method, void* delegateCls, bool* pfIsOpenDelegate); @@ -376,12 +376,10 @@ class JitInterfaceWrapper } virtual int getUnmanagedCallConv( - void* method, - void* callSiteSig, - bool* pSuppressGCTransition) + void* method) { CorInfoExceptionClass* pException = nullptr; - int temp = _callbacks->getUnmanagedCallConv(_thisHandle, &pException, method, callSiteSig, pSuppressGCTransition); + int temp = _callbacks->getUnmanagedCallConv(_thisHandle, &pException, method); if (pException != nullptr) throw pException; return temp; } diff --git a/src/coreclr/vm/amd64/cgencpu.h b/src/coreclr/vm/amd64/cgencpu.h index c7ecc88d2c4d2e..0115cf7acb6687 100644 --- a/src/coreclr/vm/amd64/cgencpu.h +++ b/src/coreclr/vm/amd64/cgencpu.h @@ -419,6 +419,20 @@ extern "C" void getFPReturn(int fpSize, INT64 *retval); struct ComToManagedExRecord; // defined in cgencpu.cpp +inline BOOL IsUnmanagedValueTypeReturnedByRef(UINT sizeofvaluetype) +{ + LIMITED_METHOD_CONTRACT; + + if (sizeofvaluetype > ENREGISTERED_RETURNTYPE_MAXSIZE) + { + return TRUE; + } + else + { + return FALSE; + } +} + #include struct DECLSPEC_ALIGN(8) UMEntryThunkCode { diff --git a/src/coreclr/vm/arm/cgencpu.h b/src/coreclr/vm/arm/cgencpu.h index 3739f4ce5dc662..51cb67acba0def 100644 --- a/src/coreclr/vm/arm/cgencpu.h +++ b/src/coreclr/vm/arm/cgencpu.h @@ -929,6 +929,17 @@ class StubLinkerCPU : public StubLinker extern "C" void SinglecastDelegateInvokeStub(); +// SEH info forward declarations + +inline BOOL IsUnmanagedValueTypeReturnedByRef(UINT sizeofvaluetype) +{ + LIMITED_METHOD_CONTRACT; + + // structure that dont fit in the machine-word size are returned + // by reference. + return (sizeofvaluetype > 4); +} + #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable:4359) // Prevent "warning C4359: 'UMEntryThunkCode': Alignment specifier is less than actual alignment (8), and will be ignored." in crossbitness scenario diff --git a/src/coreclr/vm/arm64/cgencpu.h b/src/coreclr/vm/arm64/cgencpu.h index b28ac4d99c9fe2..d8b4281073afb9 100644 --- a/src/coreclr/vm/arm64/cgencpu.h +++ b/src/coreclr/vm/arm64/cgencpu.h @@ -338,6 +338,15 @@ inline PCODE decodeBackToBackJump(PCODE pBuffer) return decodeJump(pBuffer); } +// SEH info forward declarations + +inline BOOL IsUnmanagedValueTypeReturnedByRef(UINT sizeofvaluetype) +{ + // ARM64TODO: Does this need to care about HFA. It does not for ARM32 + return (sizeofvaluetype > ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE); +} + + //---------------------------------------------------------------------- struct IntReg diff --git a/src/coreclr/vm/dllimport.cpp b/src/coreclr/vm/dllimport.cpp index 187a344b85422c..4cae3832604f3b 100644 --- a/src/coreclr/vm/dllimport.cpp +++ b/src/coreclr/vm/dllimport.cpp @@ -1598,7 +1598,7 @@ void NDirectStubLinker::SetCallingConvention(CorPinvokeMap unmngCallConv, BOOL f { // The JIT has to use a different calling convention for unmanaged vararg targets on 64-bit and ARM: // any float values must be duplicated in the corresponding general-purpose registers. - uNativeCallingConv = IMAGE_CEE_CS_CALLCONV_NATIVEVARARG; + uNativeCallingConv = CORINFO_CALLCONV_NATIVEVARARG; } else #endif // !TARGET_X86 @@ -1606,17 +1606,17 @@ void NDirectStubLinker::SetCallingConvention(CorPinvokeMap unmngCallConv, BOOL f switch (unmngCallConv) { case pmCallConvCdecl: - uNativeCallingConv = IMAGE_CEE_CS_CALLCONV_C; + uNativeCallingConv = CORINFO_CALLCONV_C; break; case pmCallConvStdcall: - uNativeCallingConv = IMAGE_CEE_CS_CALLCONV_STDCALL; + uNativeCallingConv = CORINFO_CALLCONV_STDCALL; break; case pmCallConvThiscall: - uNativeCallingConv = IMAGE_CEE_CS_CALLCONV_THISCALL; + uNativeCallingConv = CORINFO_CALLCONV_THISCALL; break; default: _ASSERTE(!"Invalid calling convention."); - uNativeCallingConv = IMAGE_CEE_CS_CALLCONV_STDCALL; + uNativeCallingConv = CORINFO_CALLCONV_STDCALL; break; } } @@ -2980,7 +2980,7 @@ namespace HRESULT hr = MetaSig::TryGetUnmanagedCallingConventionFromModOpt(pModule, pSig, cSig, &callConvMaybe, errorResID); if (hr != S_OK) return hr; - + if (!TryConvertCallConvValueToPInvokeCallConv(callConvMaybe, pPinvokeMapOut)) return S_FALSE; @@ -3228,7 +3228,18 @@ BOOL NDirect::MarshalingRequired( return TRUE; } #endif - if (i > 0) + + // return value is fine as long as it can be normalized to an integer + if (i == 0) + { + CorElementType normalizedType = hndArgType.GetInternalCorElementType(); + if (normalizedType == ELEMENT_TYPE_VALUETYPE) + { + // it is a structure even after normalization + return TRUE; + } + } + else { dwStackSize += StackElemSize(hndArgType.GetSize()); } @@ -3666,6 +3677,35 @@ static void CreateNDirectStubWorker(StubState* pss, } } + if (marshalType == MarshalInfo::MARSHAL_TYPE_DATE || + marshalType == MarshalInfo::MARSHAL_TYPE_CURRENCY || + marshalType == MarshalInfo::MARSHAL_TYPE_ARRAYWITHOFFSET || + marshalType == MarshalInfo::MARSHAL_TYPE_HANDLEREF || + marshalType == MarshalInfo::MARSHAL_TYPE_ARGITERATOR +#ifdef FEATURE_COMINTEROP + || marshalType == MarshalInfo::MARSHAL_TYPE_OLECOLOR +#endif // FEATURE_COMINTEROP + ) + { + // These are special non-blittable types returned by-ref in managed, + // but marshaled as primitive values returned by-value in unmanaged. + } + else + { + // This is an ordinary value type - see if it is returned by-ref. + TypeHandle retType = msig.GetRetTypeHandleThrowing(); + if (retType.IsValueType() && !retType.IsEnum() && IsUnmanagedValueTypeReturnedByRef(retType.MakeNativeValueType().GetSize())) + { + nativeStackSize += sizeof(LPVOID); + } +#if defined(TARGET_WINDOWS) && !defined(TARGET_ARM) + else if (fThisCall && !retType.IsEnum()) + { + nativeStackSize += sizeof(LPVOID); + } +#endif + } + if (SF_IsHRESULTSwapping(dwStubFlags)) { if (msig.GetReturnType() != ELEMENT_TYPE_VOID) @@ -4201,6 +4241,27 @@ void NDirect::PopulateNDirectMethodDesc(NDirectMethodDesc* pNMD, PInvokeStaticSi pNMD->ndirect.m_pszEntrypointName.SetValueMaybeNull(szEntryPointName); } +#ifdef TARGET_X86 + if (ndirectflags & NDirectMethodDesc::kStdCall) + { + // Compute the kStdCallWithRetBuf flag which is needed at link time for entry point mangling. + MetaSig msig(pNMD); + ArgIterator argit(&msig); + if (argit.HasRetBuffArg()) + { + MethodTable *pRetMT = msig.GetRetTypeHandleThrowing().AsMethodTable(); + // The System.DateTime type itself technically doesn't have a native representation, + // so we have to special-case it here. + // If a type doesn't have a native representation, we won't set this flag. + // We'll throw an exception later when setting up the marshalling. + if (pRetMT != CoreLibBinder::GetClass(CLASS__DATE_TIME) && pRetMT->HasLayout() && IsUnmanagedValueTypeReturnedByRef(pRetMT->GetNativeSize())) + { + ndirectflags |= NDirectMethodDesc::kStdCallWithRetBuf; + } + } + } +#endif // TARGET_X86 + // Call this exactly ONCE per thread. Do not publish incomplete prestub flags // or you will introduce a race condition. pNMD->InterlockedSetNDirectFlags(ndirectflags); @@ -6161,7 +6222,7 @@ namespace #if defined(TARGET_LINUX) if (g_coreclr_embedded) { - // this matches exactly the names in Interop.Libraries.cs + // this matches exactly the names in Interop.Libraries.cs static const LPCWSTR toRedirect[] = { W("libSystem.Native"), W("libSystem.Net.Security.Native"), diff --git a/src/coreclr/vm/dllimportcallback.cpp b/src/coreclr/vm/dllimportcallback.cpp index 1a50a5e7c98981..2a5c72510f04b6 100644 --- a/src/coreclr/vm/dllimportcallback.cpp +++ b/src/coreclr/vm/dllimportcallback.cpp @@ -655,6 +655,128 @@ VOID UMEntryThunk::CompileUMThunkWorker(UMThunkStubInfo *pInfo, pcpusl->X86EmitNearJump(pEnableRejoin); } +namespace +{ + // Templated function to compute if a char string begins with a constant string. + template + bool BeginsWith(ULONG s1Len, const char* s1, const char (&s2)[S2LEN]) + { + WRAPPER_NO_CONTRACT; + + ULONG s2Len = (ULONG)S2LEN - 1; // Remove null + if (s1Len < s2Len) + return false; + + return (0 == strncmp(s1, s2, s2Len)); + } +} + +VOID UMThunkMarshInfo::SetUpForUnmanagedCallersOnly() +{ + STANDARD_VM_CONTRACT; + + MethodDesc* pMD = GetMethod(); + _ASSERTE(pMD != NULL && pMD->HasUnmanagedCallersOnlyAttribute()); + + // Validate usage + COMDelegate::ThrowIfInvalidUnmanagedCallersOnlyUsage(pMD); + + BYTE* pData = NULL; + LONG cData = 0; + + bool nativeCallableInternalData = false; + HRESULT hr = pMD->GetCustomAttribute(WellKnownAttribute::UnmanagedCallersOnly, (const VOID **)(&pData), (ULONG *)&cData); + if (hr == S_FALSE) + { + hr = pMD->GetCustomAttribute(WellKnownAttribute::NativeCallableInternal, (const VOID **)(&pData), (ULONG *)&cData); + nativeCallableInternalData = SUCCEEDED(hr); + } + + IfFailThrow(hr); + + _ASSERTE(cData > 0); + + CustomAttributeParser ca(pData, cData); + + // UnmanagedCallersOnly and NativeCallableInternal each + // have optional named arguments. + CaNamedArg namedArgs[2]; + + // For the UnmanagedCallersOnly scenario. + CaType caCallConvs; + + // Define attribute specific optional named properties + if (nativeCallableInternalData) + { + namedArgs[0].InitI4FieldEnum("CallingConvention", "System.Runtime.InteropServices.CallingConvention", (ULONG)(CorPinvokeMap)0); + } + else + { + caCallConvs.Init(SERIALIZATION_TYPE_SZARRAY, SERIALIZATION_TYPE_TYPE, SERIALIZATION_TYPE_UNDEFINED, NULL, 0); + namedArgs[0].Init("CallConvs", SERIALIZATION_TYPE_SZARRAY, caCallConvs); + } + + // Define common optional named properties + CaTypeCtor caEntryPoint(SERIALIZATION_TYPE_STRING); + namedArgs[1].Init("EntryPoint", SERIALIZATION_TYPE_STRING, caEntryPoint); + + InlineFactory, 4> caValueArrayFactory; + DomainAssembly* domainAssembly = pMD->GetLoaderModule()->GetDomainAssembly(); + IfFailThrow(Attribute::ParseAttributeArgumentValues( + pData, + cData, + &caValueArrayFactory, + NULL, + 0, + namedArgs, + lengthof(namedArgs), + domainAssembly)); + + // If the value isn't defined, then return without setting anything. + if (namedArgs[0].val.type.tag == SERIALIZATION_TYPE_UNDEFINED) + return; + + CorPinvokeMap callConvLocal = (CorPinvokeMap)0; + if (nativeCallableInternalData) + { + callConvLocal = (CorPinvokeMap)(namedArgs[0].val.u4 << 8); + } + else + { + // Set WinAPI as the default + callConvLocal = CorPinvokeMap::pmCallConvWinapi; + + CaValue* arrayOfTypes = &namedArgs[0].val; + for (ULONG i = 0; i < arrayOfTypes->arr.length; i++) + { + CaValue& typeNameValue = arrayOfTypes->arr[i]; + + // According to ECMA-335, type name strings are UTF-8. Since we are + // looking for type names that are equivalent in ASCII and UTF-8, + // using a const char constant is acceptable. Type name strings are + // in Fully Qualified form, so we include the ',' delimiter. + if (BeginsWith(typeNameValue.str.cbStr, typeNameValue.str.pStr, "System.Runtime.CompilerServices.CallConvCdecl,")) + { + callConvLocal = CorPinvokeMap::pmCallConvCdecl; + } + else if (BeginsWith(typeNameValue.str.cbStr, typeNameValue.str.pStr, "System.Runtime.CompilerServices.CallConvStdcall,")) + { + callConvLocal = CorPinvokeMap::pmCallConvStdcall; + } + else if (BeginsWith(typeNameValue.str.cbStr, typeNameValue.str.pStr, "System.Runtime.CompilerServices.CallConvFastcall,")) + { + callConvLocal = CorPinvokeMap::pmCallConvFastcall; + } + else if (BeginsWith(typeNameValue.str.cbStr, typeNameValue.str.pStr, "System.Runtime.CompilerServices.CallConvThiscall,")) + { + callConvLocal = CorPinvokeMap::pmCallConvThiscall; + } + } + } + + m_callConv = (UINT16)callConvLocal; +} + // Compiles an unmanaged to managed thunk for the given signature. Stub *UMThunkMarshInfo::CompileNExportThunk(LoaderHeap *pLoaderHeap, PInvokeStaticSigInfo* pSigInfo, MetaSig *pMetaSig, BOOL fNoStub) { @@ -1226,11 +1348,7 @@ VOID UMThunkMarshInfo::RunTimeInit() if (pMD != NULL && pMD->HasUnmanagedCallersOnlyAttribute()) { - CorPinvokeMap callConv; - if (TryGetCallingConventionFromUnmanagedCallersOnly(pMD, &callConv)) - { - m_callConv = (UINT16)callConv; - } + SetUpForUnmanagedCallersOnly(); } #endif // TARGET_X86 && !FEATURE_STUBS_AS_IL @@ -1494,124 +1612,4 @@ void STDCALL LogUMTransition(UMEntryThunk* thunk) } #endif -namespace -{ - // Templated function to compute if a char string begins with a constant string. - template - bool BeginsWith(ULONG s1Len, const char* s1, const char (&s2)[S2LEN]) - { - WRAPPER_NO_CONTRACT; - - ULONG s2Len = (ULONG)S2LEN - 1; // Remove null - if (s1Len < s2Len) - return false; - - return (0 == strncmp(s1, s2, s2Len)); - } -} - -bool TryGetCallingConventionFromUnmanagedCallersOnly(MethodDesc* pMD, CorPinvokeMap* pCallConv) -{ - STANDARD_VM_CONTRACT; - _ASSERTE(pMD != NULL && pMD->HasUnmanagedCallersOnlyAttribute()); - - // Validate usage - COMDelegate::ThrowIfInvalidUnmanagedCallersOnlyUsage(pMD); - - BYTE* pData = NULL; - LONG cData = 0; - - bool nativeCallableInternalData = false; - HRESULT hr = pMD->GetCustomAttribute(WellKnownAttribute::UnmanagedCallersOnly, (const VOID **)(&pData), (ULONG *)&cData); - if (hr == S_FALSE) - { - hr = pMD->GetCustomAttribute(WellKnownAttribute::NativeCallableInternal, (const VOID **)(&pData), (ULONG *)&cData); - nativeCallableInternalData = SUCCEEDED(hr); - } - - IfFailThrow(hr); - - _ASSERTE(cData > 0); - - CustomAttributeParser ca(pData, cData); - - // UnmanagedCallersOnly and NativeCallableInternal each - // have optional named arguments. - CaNamedArg namedArgs[2]; - - // For the UnmanagedCallersOnly scenario. - CaType caCallConvs; - - // Define attribute specific optional named properties - if (nativeCallableInternalData) - { - namedArgs[0].InitI4FieldEnum("CallingConvention", "System.Runtime.InteropServices.CallingConvention", (ULONG)(CorPinvokeMap)0); - namedArgs[0].InitI4FieldEnum("CallingConvention", "System.Runtime.InteropServices.CallingConvention", (ULONG)(CorPinvokeMap)0); - } - else - { - caCallConvs.Init(SERIALIZATION_TYPE_SZARRAY, SERIALIZATION_TYPE_TYPE, SERIALIZATION_TYPE_UNDEFINED, NULL, 0); - namedArgs[0].Init("CallConvs", SERIALIZATION_TYPE_SZARRAY, caCallConvs); - } - - // Define common optional named properties - CaTypeCtor caEntryPoint(SERIALIZATION_TYPE_STRING); - namedArgs[1].Init("EntryPoint", SERIALIZATION_TYPE_STRING, caEntryPoint); - - InlineFactory, 4> caValueArrayFactory; - DomainAssembly* domainAssembly = pMD->GetLoaderModule()->GetDomainAssembly(); - IfFailThrow(Attribute::ParseAttributeArgumentValues( - pData, - cData, - &caValueArrayFactory, - NULL, - 0, - namedArgs, - lengthof(namedArgs), - domainAssembly)); - - // If the value isn't defined, then return without setting anything. - if (namedArgs[0].val.type.tag == SERIALIZATION_TYPE_UNDEFINED) - return false; - - CorPinvokeMap callConvLocal = (CorPinvokeMap)0; - if (nativeCallableInternalData) - { - callConvLocal = (CorPinvokeMap)(namedArgs[0].val.u4 << 8); - } - else - { - // Set WinAPI as the default - callConvLocal = CorPinvokeMap::pmCallConvWinapi; - - CaValue* arrayOfTypes = &namedArgs[0].val; - for (ULONG i = 0; i < arrayOfTypes->arr.length; i++) - { - CaValue& typeNameValue = arrayOfTypes->arr[i]; - - // According to ECMA-335, type name strings are UTF-8. Since we are - // looking for type names that are equivalent in ASCII and UTF-8, - // using a const char constant is acceptable. Type name strings are - // in Fully Qualified form, so we include the ',' delimiter. - if (BeginsWith(typeNameValue.str.cbStr, typeNameValue.str.pStr, "System.Runtime.CompilerServices.CallConvCdecl,")) - { - callConvLocal = CorPinvokeMap::pmCallConvCdecl; - } - else if (BeginsWith(typeNameValue.str.cbStr, typeNameValue.str.pStr, "System.Runtime.CompilerServices.CallConvStdcall,")) - { - callConvLocal = CorPinvokeMap::pmCallConvStdcall; - } - else if (BeginsWith(typeNameValue.str.cbStr, typeNameValue.str.pStr, "System.Runtime.CompilerServices.CallConvFastcall,")) - { - callConvLocal = CorPinvokeMap::pmCallConvFastcall; - } - else if (BeginsWith(typeNameValue.str.cbStr, typeNameValue.str.pStr, "System.Runtime.CompilerServices.CallConvThiscall,")) - { - callConvLocal = CorPinvokeMap::pmCallConvThiscall; - } - } - } - *pCallConv = callConvLocal; - return true; -} #endif // CROSSGEN_COMPILE diff --git a/src/coreclr/vm/dllimportcallback.h b/src/coreclr/vm/dllimportcallback.h index f0628da5b4fd04..1bdb5d5cbebd32 100644 --- a/src/coreclr/vm/dllimportcallback.h +++ b/src/coreclr/vm/dllimportcallback.h @@ -177,6 +177,8 @@ class UMThunkMarshInfo VOID SetupArguments(char *pSrc, ArgumentRegisters *pArgRegs, char *pDst); #else private: + VOID SetUpForUnmanagedCallersOnly(); + // Compiles an unmanaged to managed thunk for the given signature. The thunk // will call the stub or, if fNoStub == TRUE, directly the managed target. Stub *CompileNExportThunk(LoaderHeap *pLoaderHeap, PInvokeStaticSigInfo* pSigInfo, MetaSig *pMetaSig, BOOL fNoStub); @@ -527,8 +529,6 @@ EXCEPTION_HANDLER_DECL(UMThunkPrestubHandler); #endif // TARGET_X86 && !FEATURE_STUBS_AS_IL -bool TryGetCallingConventionFromUnmanagedCallersOnly(MethodDesc* pMD, CorPinvokeMap* pCallConv); - extern "C" void TheUMEntryPrestub(void); extern "C" PCODE TheUMEntryPrestubWorker(UMEntryThunk * pUMEntryThunk); diff --git a/src/coreclr/vm/i386/cgencpu.h b/src/coreclr/vm/i386/cgencpu.h index 7a0ef55aec80a6..0fb8b70deb22f4 100644 --- a/src/coreclr/vm/i386/cgencpu.h +++ b/src/coreclr/vm/i386/cgencpu.h @@ -453,6 +453,21 @@ EXTERN_C void __stdcall getFPReturn(int fpSize, INT64 *pretval); // SEH info forward declarations +inline BOOL IsUnmanagedValueTypeReturnedByRef(UINT sizeofvaluetype) +{ + LIMITED_METHOD_CONTRACT; + +#ifndef UNIX_X86_ABI + // odd-sized small structures are not + // enregistered e.g. struct { char a,b,c; } + return (sizeofvaluetype > 8) || + (sizeofvaluetype & (sizeofvaluetype - 1)); // check that the size is power of two +#else + // For UNIX_X86_ABI, we always return the value type by reference regardless of its size + return true; +#endif +} + #include struct DECLSPEC_ALIGN(4) UMEntryThunkCode { diff --git a/src/coreclr/vm/interpreter.cpp b/src/coreclr/vm/interpreter.cpp index 325aaf4590b9e7..5fd9830b00f433 100644 --- a/src/coreclr/vm/interpreter.cpp +++ b/src/coreclr/vm/interpreter.cpp @@ -384,20 +384,20 @@ void InterpreterMethodInfo::InitArgInfo(CEEInfo* comp, CORINFO_METHOD_INFO* meth } break; - case IMAGE_CEE_CS_CALLCONV_C: - NYI_INTERP("InterpreterMethodInfo::InitArgInfo -- IMAGE_CEE_CS_CALLCONV_C"); + case CORINFO_CALLCONV_C: + NYI_INTERP("InterpreterMethodInfo::InitArgInfo -- CORINFO_CALLCONV_C"); break; - case IMAGE_CEE_CS_CALLCONV_STDCALL: - NYI_INTERP("InterpreterMethodInfo::InitArgInfo -- IMAGE_CEE_CS_CALLCONV_STDCALL"); + case CORINFO_CALLCONV_STDCALL: + NYI_INTERP("InterpreterMethodInfo::InitArgInfo -- CORINFO_CALLCONV_STDCALL"); break; - case IMAGE_CEE_CS_CALLCONV_THISCALL: - NYI_INTERP("InterpreterMethodInfo::InitArgInfo -- IMAGE_CEE_CS_CALLCONV_THISCALL"); + case CORINFO_CALLCONV_THISCALL: + NYI_INTERP("InterpreterMethodInfo::InitArgInfo -- CORINFO_CALLCONV_THISCALL"); break; - case IMAGE_CEE_CS_CALLCONV_FASTCALL: - NYI_INTERP("InterpreterMethodInfo::InitArgInfo -- IMAGE_CEE_CS_CALLCONV_FASTCALL"); + case CORINFO_CALLCONV_FASTCALL: + NYI_INTERP("InterpreterMethodInfo::InitArgInfo -- CORINFO_CALLCONV_FASTCALL"); break; case CORINFO_CALLCONV_FIELD: @@ -1261,20 +1261,20 @@ CorJitResult Interpreter::GenerateInterpreterStub(CEEInfo* comp, } break; - case IMAGE_CEE_CS_CALLCONV_C: - NYI_INTERP("GenerateInterpreterStub -- IMAGE_CEE_CS_CALLCONV_C"); + case CORINFO_CALLCONV_C: + NYI_INTERP("GenerateInterpreterStub -- CORINFO_CALLCONV_C"); break; - case IMAGE_CEE_CS_CALLCONV_STDCALL: - NYI_INTERP("GenerateInterpreterStub -- IMAGE_CEE_CS_CALLCONV_STDCALL"); + case CORINFO_CALLCONV_STDCALL: + NYI_INTERP("GenerateInterpreterStub -- CORINFO_CALLCONV_STDCALL"); break; - case IMAGE_CEE_CS_CALLCONV_THISCALL: - NYI_INTERP("GenerateInterpreterStub -- IMAGE_CEE_CS_CALLCONV_THISCALL"); + case CORINFO_CALLCONV_THISCALL: + NYI_INTERP("GenerateInterpreterStub -- CORINFO_CALLCONV_THISCALL"); break; - case IMAGE_CEE_CS_CALLCONV_FASTCALL: - NYI_INTERP("GenerateInterpreterStub -- IMAGE_CEE_CS_CALLCONV_FASTCALL"); + case CORINFO_CALLCONV_FASTCALL: + NYI_INTERP("GenerateInterpreterStub -- CORINFO_CALLCONV_FASTCALL"); break; case CORINFO_CALLCONV_FIELD: diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 10a1d5c5b7eab0..579259d000c6e5 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -24,7 +24,6 @@ #include "float.h" // for isnan #include "dbginterface.h" #include "dllimport.h" -#include "dllimportcallback.h" #include "gcheaputilities.h" #include "comdelegate.h" #include "corprof.h" @@ -452,16 +451,29 @@ CEEInfo::ConvToJitSig( DWORD cbSig, CORINFO_MODULE_HANDLE scopeHnd, mdToken token, - SigTypeContext* typeContext, - ConvToJitSigFlags flags, - CORINFO_SIG_INFO * sigRet) + CORINFO_SIG_INFO * sigRet, + MethodDesc * pContextMD, + bool localSig, + TypeHandle contextType) { CONTRACTL { THROWS; GC_TRIGGERS; } CONTRACTL_END; + SigTypeContext typeContext; + uint32_t sigRetFlags = 0; + if (pContextMD) + { + SigTypeContext::InitTypeContext(pContextMD, contextType, &typeContext); + if (pContextMD->ShouldSuppressGCTransition()) + sigRetFlags |= CORINFO_SIGFLAG_SUPPRESS_GC_TRANSITION; + } + else + { + SigTypeContext::InitTypeContext(contextType, &typeContext); + } static_assert_no_msg(CORINFO_CALLCONV_DEFAULT == (CorInfoCallConv) IMAGE_CEE_CS_CALLCONV_DEFAULT); static_assert_no_msg(CORINFO_CALLCONV_VARARG == (CorInfoCallConv) IMAGE_CEE_CS_CALLCONV_VARARG); @@ -476,14 +488,14 @@ CEEInfo::ConvToJitSig( sigRet->retTypeSigClass = 0; sigRet->scope = scopeHnd; sigRet->token = token; - sigRet->sigInst.classInst = (CORINFO_CLASS_HANDLE *) typeContext->m_classInst.GetRawArgs(); - sigRet->sigInst.classInstCount = (unsigned) typeContext->m_classInst.GetNumArgs(); - sigRet->sigInst.methInst = (CORINFO_CLASS_HANDLE *) typeContext->m_methodInst.GetRawArgs(); - sigRet->sigInst.methInstCount = (unsigned) typeContext->m_methodInst.GetNumArgs(); + sigRet->sigInst.classInst = (CORINFO_CLASS_HANDLE *) typeContext.m_classInst.GetRawArgs(); + sigRet->sigInst.classInstCount = (unsigned) typeContext.m_classInst.GetNumArgs(); + sigRet->sigInst.methInst = (CORINFO_CLASS_HANDLE *) typeContext.m_methodInst.GetRawArgs(); + sigRet->sigInst.methInstCount = (unsigned) typeContext.m_methodInst.GetNumArgs(); SigPointer sig(pSig, cbSig); - if ((flags & CONV_TO_JITSIG_FLAGS_LOCALSIG) == 0) + if (!localSig) { // This is a method signature which includes calling convention, return type, // arguments, etc @@ -504,6 +516,30 @@ CEEInfo::ConvToJitSig( } #endif // defined(TARGET_UNIX) || defined(TARGET_ARM) + // Unmanaged calling convention indicates modopt should be read + if (sigRet->callConv == CORINFO_CALLCONV_UNMANAGED) + { + static_assert_no_msg(CORINFO_CALLCONV_C == (CorInfoCallConv)IMAGE_CEE_UNMANAGED_CALLCONV_C); + static_assert_no_msg(CORINFO_CALLCONV_STDCALL == (CorInfoCallConv)IMAGE_CEE_UNMANAGED_CALLCONV_STDCALL); + static_assert_no_msg(CORINFO_CALLCONV_THISCALL == (CorInfoCallConv)IMAGE_CEE_UNMANAGED_CALLCONV_THISCALL); + static_assert_no_msg(CORINFO_CALLCONV_FASTCALL == (CorInfoCallConv)IMAGE_CEE_UNMANAGED_CALLCONV_FASTCALL); + + CorUnmanagedCallingConvention callConvMaybe; + UINT errorResID; + HRESULT hr = MetaSig::TryGetUnmanagedCallingConventionFromModOpt(module, pSig, cbSig, &callConvMaybe, &errorResID); + if (FAILED(hr)) + COMPlusThrowHR(hr, errorResID); + + if (hr == S_OK) + { + sigRet->callConv = (CorInfoCallConv)callConvMaybe; + } + else + { + sigRet->callConv = (CorInfoCallConv)MetaSig::GetDefaultUnmanagedCallingConvention(); + } + } + // Skip number of type arguments if (sigRet->callConv & IMAGE_CEE_CS_CALLCONV_GENERIC) IfFailThrow(sig.GetData(NULL)); @@ -515,11 +551,11 @@ CEEInfo::ConvToJitSig( sigRet->numArgs = (unsigned short) numArgs; - CorElementType type = sig.PeekElemTypeClosed(module, typeContext); + CorElementType type = sig.PeekElemTypeClosed(module, &typeContext); if (!CorTypeInfo::IsPrimitiveType(type)) { - typeHnd = sig.GetTypeHandleThrowing(module, typeContext); + typeHnd = sig.GetTypeHandleThrowing(module, &typeContext); _ASSERTE(!typeHnd.IsNull()); // I believe it doesn't make any diff. if this is @@ -564,6 +600,8 @@ CEEInfo::ConvToJitSig( sigRet->args = (CORINFO_ARG_LIST_HANDLE)sig.GetPtr(); } + _ASSERTE(sigRet->callConv != CORINFO_CALLCONV_UNMANAGED); + // Set computed flags sigRet->flags = sigRetFlags; @@ -1844,17 +1882,15 @@ CEEInfo::findCallSiteSig( } } - SigTypeContext typeContext; - GetTypeContext(context, &typeContext); - CEEInfo::ConvToJitSig( pSig, cbSig, scopeHnd, sigMethTok, - &typeContext, - CONV_TO_JITSIG_FLAGS_NONE, - sigRet); + sigRet, + GetMethodFromContext(context), + false, + GetTypeFromContext(context)); EE_TO_JIT_TRANSITION(); } // CEEInfo::findCallSiteSig @@ -1895,17 +1931,15 @@ CEEInfo::findSig( &pSig)); } - SigTypeContext typeContext; - GetTypeContext(context, &typeContext); - CEEInfo::ConvToJitSig( pSig, cbSig, scopeHnd, sigTok, - &typeContext, - CONV_TO_JITSIG_FLAGS_NONE, - sigRet); + sigRet, + GetMethodFromContext(context), + false, + GetTypeFromContext(context)); EE_TO_JIT_TRANSITION(); } // CEEInfo::findSig @@ -7726,8 +7760,6 @@ getMethodInfoHelper( DWORD cbSig = 0; ftn->GetSig(&pSig, &cbSig); - SigTypeContext context(ftn); - /* Fetch the method signature */ // Type parameters in the signature should be instantiated according to the // class/method/array instantiation of ftnHnd @@ -7736,9 +7768,9 @@ getMethodInfoHelper( cbSig, GetScopeHandle(ftn), mdTokenNil, - &context, - CEEInfo::CONV_TO_JITSIG_FLAGS_NONE, - &methInfo->args); + &methInfo->args, + ftn, + false); // Shared generic or static per-inst methods and shared methods on generic structs // take an extra argument representing their instantiation @@ -7755,9 +7787,9 @@ getMethodInfoHelper( cbLocalSig, GetScopeHandle(ftn), mdTokenNil, - &context, - CEEInfo::CONV_TO_JITSIG_FLAGS_LOCALSIG, - &methInfo->locals); + &methInfo->locals, + ftn, + true); } // getMethodInfoHelper @@ -8590,8 +8622,6 @@ CEEInfo::getMethodSigInternal( DWORD cbSig = 0; ftn->GetSig(&pSig, &cbSig); - SigTypeContext context(ftn, (TypeHandle)owner); - // Type parameters in the signature are instantiated // according to the class/method/array instantiation of ftnHnd and owner CEEInfo::ConvToJitSig( @@ -8599,9 +8629,10 @@ CEEInfo::getMethodSigInternal( cbSig, GetScopeHandle(ftn), mdTokenNil, - &context, - CONV_TO_JITSIG_FLAGS_NONE, - sigRet); + sigRet, + ftn, + false, + (TypeHandle)owner); //@GENERICS: // Shared generic methods and shared methods on generic structs take an extra argument representing their instantiation @@ -8890,7 +8921,7 @@ bool CEEInfo::resolveVirtualMethodHelper(CORINFO_DEVIRTUALIZATION_INFO * info) return false; } - // For generic interface methods we must have context to + // For generic interface methods we must have context to // safely devirtualize. if (info->context != nullptr) { @@ -9773,149 +9804,10 @@ CorInfoHFAElemType CEEInfo::getHFAType(CORINFO_CLASS_HANDLE hClass) return result; } -namespace -{ - CorInfoCallConvExtension getUnmanagedCallConvForSig(Module* mod, PCCOR_SIGNATURE pSig, DWORD cbSig, bool* pSuppressGCTransition) - { - SigParser parser(pSig, cbSig); - ULONG rawCallConv; - if (FAILED(parser.GetCallingConv(&rawCallConv))) - { - COMPlusThrowHR(COR_E_BADIMAGEFORMAT); - } - switch ((CorCallingConvention)rawCallConv) - { - case IMAGE_CEE_CS_CALLCONV_DEFAULT: - _ASSERTE_MSG(false, "bad callconv"); - return CorInfoCallConvExtension::Managed; - case IMAGE_CEE_CS_CALLCONV_C: - return CorInfoCallConvExtension::C; - case IMAGE_CEE_CS_CALLCONV_STDCALL: - return CorInfoCallConvExtension::Stdcall; - case IMAGE_CEE_CS_CALLCONV_THISCALL: - return CorInfoCallConvExtension::Thiscall; - case IMAGE_CEE_CS_CALLCONV_FASTCALL: - return CorInfoCallConvExtension::Fastcall; - case IMAGE_CEE_CS_CALLCONV_UNMANAGED: - { - CorUnmanagedCallingConvention callConvMaybe; - UINT errorResID; - HRESULT hr = MetaSig::TryGetUnmanagedCallingConventionFromModOpt(mod, pSig, cbSig, &callConvMaybe, &errorResID); - if (FAILED(hr)) - COMPlusThrowHR(hr, errorResID); - - if (hr == S_OK) - { - return (CorInfoCallConvExtension)callConvMaybe; - } - else - { - return (CorInfoCallConvExtension)MetaSig::GetDefaultUnmanagedCallingConvention(); - } - } - case IMAGE_CEE_CS_CALLCONV_NATIVEVARARG: - return CorInfoCallConvExtension::C; - default: - _ASSERTE_MSG(false, "bad callconv"); - return CorInfoCallConvExtension::Managed; - } - } - - CorInfoCallConvExtension getUnmanagedCallConvForMethod(MethodDesc* pMD, bool* pSuppressGCTransition) - { - ULONG methodCallConv; - PCCOR_SIGNATURE pSig; - DWORD cbSig; - pMD->GetSig(&pSig, &cbSig); - if (FAILED(SigParser(pSig, cbSig).GetCallingConv(&methodCallConv))) - { - COMPlusThrowHR(COR_E_BADIMAGEFORMAT); - } - - if (methodCallConv == CORINFO_CALLCONV_DEFAULT || methodCallConv == CORINFO_CALLCONV_VARARG) - { - _ASSERTE(pMD->IsNDirect() || pMD->HasUnmanagedCallersOnlyAttribute()); - if (pMD->IsNDirect()) - { - if (pSuppressGCTransition) - { - *pSuppressGCTransition = pMD->ShouldSuppressGCTransition(); - } - - PInvokeStaticSigInfo sigInfo(pMD, PInvokeStaticSigInfo::NO_THROW_ON_ERROR); - switch (sigInfo.GetCallConv()) - { - case pmCallConvCdecl: - return CorInfoCallConvExtension::C; - break; - case pmCallConvStdcall: - return CorInfoCallConvExtension::Stdcall; - break; - case pmCallConvThiscall: - return CorInfoCallConvExtension::Thiscall; - break; - case pmCallConvFastcall: - return CorInfoCallConvExtension::Fastcall; - break; - default: - _ASSERTE_MSG(false, "bad callconv"); - return CorInfoCallConvExtension::Managed; - break; - } - } - else - { -#ifdef CROSSGEN_COMPILE - _ASSERTE_MSG(false, "UnmanagedCallersOnly methods are not supported in crossgen and should be rejected before getting here."); - return CorInfoCallConvExtension::Managed; -#else - CorPinvokeMap unmanagedCallConv; - if (TryGetCallingConventionFromUnmanagedCallersOnly(pMD, &unmanagedCallConv)) - { - if (methodCallConv == IMAGE_CEE_CS_CALLCONV_VARARG) - { - return CorInfoCallConvExtension::C; - } - switch (unmanagedCallConv) - { - case pmCallConvWinapi: - return (CorInfoCallConvExtension)MetaSig::GetDefaultUnmanagedCallingConvention(); - break; - case pmCallConvCdecl: - return CorInfoCallConvExtension::C; - break; - case pmCallConvStdcall: - return CorInfoCallConvExtension::Stdcall; - break; - case pmCallConvThiscall: - return CorInfoCallConvExtension::Thiscall; - break; - case pmCallConvFastcall: - return CorInfoCallConvExtension::Fastcall; - break; - default: - _ASSERTE_MSG(false, "bad callconv"); - break; - } - } - return (CorInfoCallConvExtension)MetaSig::GetDefaultUnmanagedCallingConvention(); -#endif // CROSSGEN_COMPILE - } - } - else - { - return getUnmanagedCallConvForSig(pMD->GetModule(), pSig, cbSig, pSuppressGCTransition); - } - } -} - /*********************************************************************/ - // return the entry point calling convention for any of the following - // - a P/Invoke - // - a method marked with UnmanagedCallersOnly - // - a function pointer with the CORINFO_CALLCONV_UNMANAGED calling convention. -CorInfoCallConvExtension CEEInfo::getUnmanagedCallConv(CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* callSiteSig, bool* pSuppressGCTransition) + // return the unmanaged calling convention for a PInvoke +CorInfoUnmanagedCallConv CEEInfo::getUnmanagedCallConv(CORINFO_METHOD_HANDLE method) { CONTRACTL { THROWS; @@ -9923,28 +9815,48 @@ CorInfoCallConvExtension CEEInfo::getUnmanagedCallConv(CORINFO_METHOD_HANDLE met MODE_PREEMPTIVE; } CONTRACTL_END; - CorInfoCallConvExtension callConv = CorInfoCallConvExtension::Managed; + CorInfoUnmanagedCallConv result = CORINFO_UNMANAGED_CALLCONV_UNKNOWN; JIT_TO_EE_TRANSITION(); - if (pSuppressGCTransition) - { - *pSuppressGCTransition = false; - } + MethodDesc* pMD = NULL; + pMD = GetMethod(method); + _ASSERTE(pMD->IsNDirect()); - if (method) +#ifdef TARGET_X86 + EX_TRY { - callConv = getUnmanagedCallConvForMethod(GetMethod(method), pSuppressGCTransition); + PInvokeStaticSigInfo sigInfo(pMD, PInvokeStaticSigInfo::NO_THROW_ON_ERROR); + + switch (sigInfo.GetCallConv()) { + case pmCallConvCdecl: + result = CORINFO_UNMANAGED_CALLCONV_C; + break; + case pmCallConvStdcall: + result = CORINFO_UNMANAGED_CALLCONV_STDCALL; + break; + case pmCallConvThiscall: + result = CORINFO_UNMANAGED_CALLCONV_THISCALL; + break; + default: + result = CORINFO_UNMANAGED_CALLCONV_UNKNOWN; + } } - else + EX_CATCH { - _ASSERTE(callSiteSig != nullptr); - callConv = getUnmanagedCallConvForSig(GetModule(callSiteSig->scope), callSiteSig->pSig, callSiteSig->cbSig, pSuppressGCTransition); + result = CORINFO_UNMANAGED_CALLCONV_UNKNOWN; } + EX_END_CATCH(SwallowAllExceptions) +#else // !TARGET_X86 + // + // we have only one calling convention + // + result = CORINFO_UNMANAGED_CALLCONV_STDCALL; +#endif // !TARGET_X86 EE_TO_JIT_TRANSITION(); - return callConv; + return result; } /*********************************************************************/ @@ -12039,7 +11951,7 @@ HRESULT CEEJitInfo::getMethodBlockCounts ( #endif EE_TO_JIT_TRANSITION(); - + return hr; } @@ -12828,7 +12740,7 @@ CORJIT_FLAGS GetCompileFlags(MethodDesc * ftn, CORJIT_FLAGS flags, CORINFO_METHO #ifdef FEATURE_PGO // Instrument, if - // + // // * We're writing pgo data and we're jitting at Tier0. // * Tiered PGO is enabled and we're jitting at Tier0. // @@ -12852,7 +12764,7 @@ CORJIT_FLAGS GetCompileFlags(MethodDesc * ftn, CORJIT_FLAGS flags, CORINFO_METHO { flags.Set(CORJIT_FLAGS::CORJIT_FLAG_BBOPT); } - + #endif return flags; @@ -13975,7 +13887,7 @@ BOOL LoadDynamicInfoEntry(Module *currentModule, // Verification failures are failfast events DefineFullyQualifiedNameForClassW(); SString fatalErrorString; - fatalErrorString.Printf(W("Verify_TypeLayout '%s' failed to verify type layout"), + fatalErrorString.Printf(W("Verify_TypeLayout '%s' failed to verify type layout"), GetFullyQualifiedNameForClassW(pMT)); #ifdef _DEBUG @@ -14028,7 +13940,7 @@ BOOL LoadDynamicInfoEntry(Module *currentModule, } DWORD actualBaseOffset = 0; - if (!pField->IsStatic() && + if (!pField->IsStatic() && pEnclosingMT->GetParentMethodTable() != NULL && !pEnclosingMT->IsValueType()) { @@ -14042,7 +13954,7 @@ BOOL LoadDynamicInfoEntry(Module *currentModule, SString ssFieldName(SString::Utf8, pField->GetName()); SString fatalErrorString; - fatalErrorString.Printf(W("Verify_FieldOffset '%s.%s' Field offset %d!=%d(actual) || baseOffset %d!=%d(actual)"), + fatalErrorString.Printf(W("Verify_FieldOffset '%s.%s' Field offset %d!=%d(actual) || baseOffset %d!=%d(actual)"), GetFullyQualifiedNameForClassW(pEnclosingMT), ssFieldName.GetUnicode(), fieldOffset, diff --git a/src/coreclr/vm/jitinterface.h b/src/coreclr/vm/jitinterface.h index 8b681c4818e573..78edf6f3a1d630 100644 --- a/src/coreclr/vm/jitinterface.h +++ b/src/coreclr/vm/jitinterface.h @@ -61,6 +61,7 @@ bool SigInfoFlagsAreValid (CORINFO_SIG_INFO *sig) LIMITED_METHOD_CONTRACT; return !(sig->flags & ~( CORINFO_SIGFLAG_IS_LOCAL_SIG | CORINFO_SIGFLAG_IL_STUB + | CORINFO_SIGFLAG_SUPPRESS_GC_TRANSITION )); } @@ -540,12 +541,6 @@ class CEEInfo : public ICorJitInfo public: - enum ConvToJitSigFlags : int - { - CONV_TO_JITSIG_FLAGS_NONE = 0x0, - CONV_TO_JITSIG_FLAGS_LOCALSIG = 0x1, - }; - //@GENERICS: // The method handle is used to instantiate method and class type parameters // It's also used to determine whether an extra dictionary parameter is required @@ -556,9 +551,10 @@ class CEEInfo : public ICorJitInfo DWORD cbSig, CORINFO_MODULE_HANDLE scopeHnd, mdToken token, - SigTypeContext* context, - ConvToJitSigFlags flags, - CORINFO_SIG_INFO * sigRet); + CORINFO_SIG_INFO * sigRet, + MethodDesc * context, + bool localSig, + TypeHandle owner = TypeHandle()); MethodDesc * GetMethodForSecurity(CORINFO_METHOD_HANDLE callerHandle); diff --git a/src/coreclr/vm/method.cpp b/src/coreclr/vm/method.cpp index 1daa37156640ca..ed6f44e7426ce5 100644 --- a/src/coreclr/vm/method.cpp +++ b/src/coreclr/vm/method.cpp @@ -5314,6 +5314,12 @@ FARPROC NDirectMethodDesc::FindEntryPointWithMangling(NATIVE_LIBRARY_HANDLE hMod UINT16 numParamBytesMangle = GetStackArgumentSize(); + if (IsStdCallWithRetBuf()) + { + _ASSERTE(numParamBytesMangle >= sizeof(LPVOID)); + numParamBytesMangle -= (UINT16)sizeof(LPVOID); + } + sprintf_s(szProbedEntrypointName + probedEntrypointNameLength, dstbufsize - probedEntrypointNameLength + 1, "@%lu", (ULONG)numParamBytesMangle); pFunc = GetProcAddress(hMod, szProbedEntrypointName); } diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp index ee1e68567f6ef0..28c6935212df7b 100644 --- a/src/coreclr/vm/method.hpp +++ b/src/coreclr/vm/method.hpp @@ -2963,6 +2963,8 @@ class NDirectMethodDesc : public MethodDesc #if defined(TARGET_X86) // Size of outgoing arguments (on stack). Note that in order to get the @n stdcall name decoration, + // it may be necessary to subtract 4 as the hidden large structure pointer parameter does not count. + // See code:kStdCallWithRetBuf WORD m_cbStackArgumentSize; #endif // defined(TARGET_X86) @@ -3012,6 +3014,9 @@ class NDirectMethodDesc : public MethodDesc kIsQCall = 0x1000, kDefaultDllImportSearchPathsStatus = 0x2000, // either method has custom attribute or not. + + kStdCallWithRetBuf = 0x8000, // Call returns large structure, only valid if kStdCall is also set + }; // Resolve the import to the NDirect target and set it on the NDirectMethodDesc. @@ -3156,6 +3161,13 @@ class NDirectMethodDesc : public MethodDesc return (ndirect.m_DefaultDllImportSearchPathsAttributeValue & 0x2) != 0; } + BOOL IsStdCallWithRetBuf() const + { + LIMITED_METHOD_DAC_CONTRACT; + + return (ndirect.m_wFlags & kStdCallWithRetBuf) != 0; + } + PTR_NDirectWriteableData GetWriteableData() const { LIMITED_METHOD_DAC_CONTRACT; diff --git a/src/coreclr/zap/zapinfo.cpp b/src/coreclr/zap/zapinfo.cpp index 557367c31c8443..431e57ac06da84 100644 --- a/src/coreclr/zap/zapinfo.cpp +++ b/src/coreclr/zap/zapinfo.cpp @@ -400,13 +400,6 @@ void ZapInfo::CompileMethod() m_zapper->Info(W("Compiling method %s\n"), m_currentMethodName.GetUnicode()); } - if (GetCompileInfo()->IsUnmanagedCallersOnlyMethod(m_currentMethodHandle)) - { - if (m_zapper->m_pOpt->m_verbose) - m_zapper->Warning(W("ReadyToRun: Methods with UnmanagedCallersOnlyAttribute not implemented\n")); - ThrowHR(E_NOTIMPL); - } - m_currentMethodInfo = CORINFO_METHOD_INFO(); if (!getMethodInfo(m_currentMethodHandle, &m_currentMethodInfo)) { @@ -481,6 +474,15 @@ void ZapInfo::CompileMethod() } #endif +#ifdef TARGET_X86 + if (GetCompileInfo()->IsUnmanagedCallersOnlyMethod(m_currentMethodHandle)) + { + if (m_zapper->m_pOpt->m_verbose) + m_zapper->Warning(W("ReadyToRun: Methods with UnmanagedCallersOnlyAttribute not implemented\n")); + ThrowHR(E_NOTIMPL); + } +#endif // TARGET_X86 + if (m_pImage->m_stats) { m_pImage->m_stats->m_methods++; @@ -2206,7 +2208,7 @@ DWORD FilterNamedIntrinsicMethodAttribs(ZapInfo* pZapInfo, DWORD attribs, CORINF } #else fTreatAsRegularMethodCall |= !fIsPlatformHWIntrinsic && fIsHWIntrinsic; -#endif +#endif if (fIsPlatformHWIntrinsic) { @@ -4030,9 +4032,9 @@ bool ZapInfo::isIntrinsicType(CORINFO_CLASS_HANDLE classHnd) return m_pEEJitInfo->isIntrinsicType(classHnd); } -CorInfoCallConvExtension ZapInfo::getUnmanagedCallConv(CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig, bool* pSuppressGCTransition) +CorInfoUnmanagedCallConv ZapInfo::getUnmanagedCallConv(CORINFO_METHOD_HANDLE method) { - return m_pEEJitInfo->getUnmanagedCallConv(method, sig, pSuppressGCTransition); + return m_pEEJitInfo->getUnmanagedCallConv(method); } bool ZapInfo::pInvokeMarshalingRequired(CORINFO_METHOD_HANDLE method, diff --git a/src/tests/Interop/PInvoke/Miscellaneous/ThisCall/ThisCallTest.cs b/src/tests/Interop/PInvoke/Miscellaneous/ThisCall/ThisCallTest.cs index 31719e8d7aed83..66d40546e91b36 100644 --- a/src/tests/Interop/PInvoke/Miscellaneous/ThisCall/ThisCallTest.cs +++ b/src/tests/Interop/PInvoke/Miscellaneous/ThisCall/ThisCallTest.cs @@ -6,7 +6,6 @@ using System.Reflection; using System.Text; using TestLibrary; -using System.Runtime.CompilerServices; unsafe class ThisCallNative { @@ -87,10 +86,6 @@ public static int Main(string[] args) Test4ByteHFAReverse(); Test4ByteNonHFAReverse(); TestEnumReverse(); - Test8ByteHFAUnmanagedCallersOnly(); - Test4ByteHFAUnmanagedCallersOnly(); - Test4ByteNonHFAUnmanagedCallersOnly(); - TestEnumUnmanagedCallersOnly(); } catch (System.Exception ex) { @@ -169,38 +164,6 @@ private static void TestEnumReverse() Assert.AreEqual(c.dummy, result); } - private static void Test8ByteHFAUnmanagedCallersOnly() - { - ThisCallNative.C c = CreateCWithUnmanagedCallersOnlyVTable(2.0f, 3.0f); - ThisCallNative.SizeF result = ThisCallNative.GetSizeFromManaged(&c); - - Assert.AreEqual(c.width, result.width); - Assert.AreEqual(c.height, result.height); - } - - private static void Test4ByteHFAUnmanagedCallersOnly() - { - ThisCallNative.C c = CreateCWithUnmanagedCallersOnlyVTable(2.0f, 3.0f); - ThisCallNative.Width result = ThisCallNative.GetWidthFromManaged(&c); - - Assert.AreEqual(c.width, result.width); - } - - private static void Test4ByteNonHFAUnmanagedCallersOnly() - { - ThisCallNative.C c = CreateCWithUnmanagedCallersOnlyVTable(2.0f, 3.0f); - ThisCallNative.IntWrapper result = ThisCallNative.GetHeightAsIntFromManaged(&c); - - Assert.AreEqual((int)c.height, result.i); - } - - private static void TestEnumUnmanagedCallersOnly() - { - ThisCallNative.C c = CreateCWithUnmanagedCallersOnlyVTable(2.0f, 3.0f); - ThisCallNative.E result = ThisCallNative.GetEFromManaged(&c); - - Assert.AreEqual(c.dummy, result); - } private static ThisCallNative.C CreateCWithManagedVTable(float width, float height) { @@ -213,17 +176,6 @@ private static ThisCallNative.C CreateCWithManagedVTable(float width, float heig }; } - private static ThisCallNative.C CreateCWithUnmanagedCallersOnlyVTable(float width, float height) - { - return new ThisCallNative.C - { - vtable = UnmanagedCallersOnlyVtable, - dummy = ThisCallNative.E.Value, - width = width, - height = height - }; - } - private static ThisCallNative.C.VtableLayout* managedVtable; private static ThisCallNative.C.VtableLayout* ManagedVtable @@ -245,56 +197,4 @@ private static ThisCallNative.C.VtableLayout* ManagedVtable return managedVtable; } } - - private static ThisCallNative.C.VtableLayout* unmanagedCallersOnlyVtable; - - private static ThisCallNative.C.VtableLayout* UnmanagedCallersOnlyVtable - { - get - { - if (unmanagedCallersOnlyVtable == null) - { - unmanagedCallersOnlyVtable = (ThisCallNative.C.VtableLayout*)Marshal.AllocHGlobal(sizeof(ThisCallNative.C.VtableLayout)); - unmanagedCallersOnlyVtable->getSize = (IntPtr)(delegate* unmanaged[Thiscall])&GetSize; - unmanagedCallersOnlyVtable->getWidth = (IntPtr)(delegate* unmanaged[Thiscall])&GetWidth; - unmanagedCallersOnlyVtable->getHeightAsInt = (IntPtr)(delegate* unmanaged[Thiscall])&GetHeightAsInt; - unmanagedCallersOnlyVtable->getE = (IntPtr)(delegate* unmanaged[Thiscall])&GetE; - } - return unmanagedCallersOnlyVtable; - } - } - - [UnmanagedCallersOnly(CallConvs = new [] {typeof(CallConvThiscall)})] - private static ThisCallNative.SizeF GetSize(ThisCallNative.C* c) - { - return new ThisCallNative.SizeF - { - width = c->width, - height = c->height - }; - } - - [UnmanagedCallersOnly(CallConvs = new [] {typeof(CallConvThiscall)})] - private static ThisCallNative.Width GetWidth(ThisCallNative.C* c) - { - return new ThisCallNative.Width - { - width = c->width - }; - } - - [UnmanagedCallersOnly(CallConvs = new [] {typeof(CallConvThiscall)})] - private static ThisCallNative.IntWrapper GetHeightAsInt(ThisCallNative.C* c) - { - return new ThisCallNative.IntWrapper - { - i = (int)c->height - }; - } - - [UnmanagedCallersOnly(CallConvs = new [] {typeof(CallConvThiscall)})] - private static ThisCallNative.E GetE(ThisCallNative.C* c) - { - return c->dummy; - } }