diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/vm/dllimport.cpp | 179 | ||||
-rw-r--r-- | src/vm/dllimport.h | 5 | ||||
-rw-r--r-- | src/vm/ilmarshalers.h | 54 | ||||
-rw-r--r-- | src/vm/stubgen.cpp | 19 | ||||
-rw-r--r-- | src/vm/stubgen.h | 18 |
5 files changed, 170 insertions, 105 deletions
diff --git a/src/vm/dllimport.cpp b/src/vm/dllimport.cpp index b200a1bccb..c83f05ce05 100644 --- a/src/vm/dllimport.cpp +++ b/src/vm/dllimport.cpp @@ -1737,7 +1737,7 @@ NDirectStubLinker::NDirectStubLinker( int iLCIDParamIdx, BOOL fTargetHasThis, BOOL fStubHasThis) - : ILStubLinker(pModule, signature, pTypeContext, pTargetMD, fTargetHasThis, fStubHasThis, !SF_IsCOMStub(dwStubFlags)), + : ILStubLinker(pModule, signature, pTypeContext, pTargetMD, fTargetHasThis, fStubHasThis, !SF_IsCOMStub(dwStubFlags), SF_IsReverseStub(dwStubFlags)), m_pCleanupFinallyBeginLabel(NULL), m_pCleanupFinallyEndLabel(NULL), m_pSkipExceptionCleanupLabel(NULL), @@ -1747,6 +1747,7 @@ NDirectStubLinker::NDirectStubLinker( m_fHasCleanupCode(FALSE), m_fHasExceptionCleanupCode(FALSE), m_fCleanupWorkListIsSetup(FALSE), + m_targetHasThis(fTargetHasThis), m_dwThreadLocalNum(-1), m_dwCleanupWorkListLocalNum(-1), m_dwRetValLocalNum(-1), @@ -3661,42 +3662,19 @@ static MarshalInfo::MarshalType DoMarshalReturnValue(MetaSig& msig, if (SF_IsCOMStub(dwStubFlags)) { - if (marshalType == MarshalInfo::MARSHAL_TYPE_VALUECLASS || - marshalType == MarshalInfo::MARSHAL_TYPE_BLITTABLEVALUECLASS || + // We don't support native methods that return VARIANTs, non-blittable structs, GUIDs, or DECIMALs directly. + if (marshalType == MarshalInfo::MARSHAL_TYPE_OBJECT || + marshalType == MarshalInfo::MARSHAL_TYPE_VALUECLASS || marshalType == MarshalInfo::MARSHAL_TYPE_GUID || marshalType == MarshalInfo::MARSHAL_TYPE_DECIMAL) { -#ifndef _TARGET_X86_ - // We cannot optimize marshalType to MARSHAL_TYPE_GENERIC_* because the JIT works with exact types - // and would refuse to compile the stub if it implicitly converted between scalars and value types (also see - // code:MarshalInfo.MarhalInfo where we do the optimization on x86). We want to throw only if the structure - // is too big to be returned in registers. - if (marshalType != MarshalInfo::MARSHAL_TYPE_BLITTABLEVALUECLASS || - IsUnmanagedValueTypeReturnedByRef(returnInfo.GetNativeArgSize())) -#endif // _TARGET_X86_ + if (!SF_IsHRESULTSwapping(dwStubFlags) && !SF_IsCOMLateBoundStub(dwStubFlags)) { - if (!SF_IsHRESULTSwapping(dwStubFlags) && !SF_IsCOMLateBoundStub(dwStubFlags)) - { - // Note that this limitation is very likely not needed anymore and could be lifted if we care. - COMPlusThrow(kMarshalDirectiveException, IDS_EE_COM_UNSUPPORTED_SIG); - } + COMPlusThrow(kMarshalDirectiveException, IDS_EE_COM_UNSUPPORTED_SIG); } - - pss->MarshalReturn(&returnInfo, argOffset); } - else - { - // We don't support native methods that return VARIANTs directly. - if (marshalType == MarshalInfo::MARSHAL_TYPE_OBJECT) - { - if (!SF_IsHRESULTSwapping(dwStubFlags) && !SF_IsCOMLateBoundStub(dwStubFlags)) - { - COMPlusThrow(kMarshalDirectiveException, IDS_EE_COM_UNSUPPORTED_SIG); - } - } - pss->MarshalReturn(&returnInfo, argOffset); - } + pss->MarshalReturn(&returnInfo, argOffset); } else #endif // FEATURE_COMINTEROP @@ -3953,14 +3931,21 @@ static void CreateNDirectStubWorker(StubState* pss, // Normally we would like this to be false so that we use the correct signature // in the IL_STUB, (i.e if it returns a value class then the signature will use that) // When this bool is true we change the return type to void and explicitly add a - // return buffer argument as the first argument. - BOOL fMarshalReturnValueFirst = false; + // return buffer argument as the first argument so as to match the native calling convention correctly. + BOOL fMarshalReturnValueFirst = FALSE; + + BOOL fReverseWithReturnBufferArg = FALSE; // We can only change fMarshalReturnValueFirst to true when we are NOT doing HRESULT-swapping! - // + // When we are HRESULT-swapping, the managed return type is actually the type of the last parameter and not the return type. + // The native return type of an HRESULT-swapped function is an HRESULT, which never uses a return-buffer argument. + // Since the managed return type is actually the last parameter, we need to marshal it after the last parameter in the managed signature + // to make sure we match the native signature correctly (when marshalling parameters, we add them to the native stub signature). if (!SF_IsHRESULTSwapping(dwStubFlags)) { - + bool isInstanceMethod = fStubNeedsCOM || fThisCall; + // We cannot just use pSig.GetReturnType() here since it will return ELEMENT_TYPE_VALUETYPE for enums. + bool isReturnTypeValueType = msig.GetRetTypeHandleThrowing().GetVerifierCorElementType() == ELEMENT_TYPE_VALUETYPE; #if defined(_TARGET_X86_) || defined(_TARGET_ARM_) // JIT32 has problems in generating code for pinvoke ILStubs which do a return in return buffer. // Therefore instead we change the signature of calli to return void and make the return buffer as first @@ -3972,55 +3957,18 @@ static void CreateNDirectStubWorker(StubState* pss, #ifdef UNIX_X86_ABI // For functions with value type class, managed and unmanaged calling convention differ fMarshalReturnValueFirst = HasRetBuffArgUnmanagedFixup(&msig); -#else // UNIX_X86_ABI +#elif defined(_TARGET_ARM_) fMarshalReturnValueFirst = HasRetBuffArg(&msig); +#else + // On Windows-X86, the native signature might need a return buffer when the managed doesn't (specifically when the native signature is a member function). + fMarshalReturnValueFirst = HasRetBuffArg(&msig) || (isInstanceMethod && isReturnTypeValueType); #endif // UNIX_X86_ABI - +#elif defined(_TARGET_AMD64_) + fMarshalReturnValueFirst = isInstanceMethod && isReturnTypeValueType; #endif // defined(_TARGET_X86_) || defined(_TARGET_ARM_) - - } - - if (fMarshalReturnValueFirst) - { - marshalType = DoMarshalReturnValue(msig, - pParamTokenArray, - nlType, - nlFlags, - 0, - pss, - fThisCall, - argOffset, - dwStubFlags, - pMD, - nativeStackSize, - fStubNeedsCOM, - 0 - DEBUG_ARG(pSigDesc->m_pDebugName) - DEBUG_ARG(pSigDesc->m_pDebugClassName) - ); - - 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. - MethodTable *pRetMT = msig.GetRetTypeHandleThrowing().AsMethodTable(); - if (IsUnmanagedValueTypeReturnedByRef(pRetMT->GetNativeSize())) - { - nativeStackSize += sizeof(LPVOID); - } - } +#ifdef _WIN32 + fReverseWithReturnBufferArg = fMarshalReturnValueFirst && SF_IsReverseStub(dwStubFlags); +#endif } // @@ -4088,6 +4036,77 @@ static void CreateNDirectStubWorker(StubState* pss, // Marshal the parameters int argidx = 1; int nativeArgIndex = 0; + + // If we are generating a return buffer on a member function that is marked as thiscall (as opposed to being a COM method) + // then we need to marshal the this parameter first and the return buffer second. + // We don't need to do this for COM methods because the "this" is implied as argument 0 by the signature of the stub. + if (fThisCall && fMarshalReturnValueFirst) + { + msig.NextArg(); + + MarshalInfo &info = pParamMarshalInfo[argidx - 1]; + pss->MarshalArgument(&info, argOffset, GetStackOffsetFromStackSize(nativeStackSize, fThisCall)); + nativeStackSize += info.GetNativeArgSize(); + + fStubNeedsCOM |= info.MarshalerRequiresCOM(); + + // make sure that the first parameter is enregisterable + if (info.GetNativeArgSize() > sizeof(SLOT)) + COMPlusThrow(kMarshalDirectiveException, IDS_EE_NDIRECT_BADNATL_THISCALL); + + argidx++; + } + + // If we're doing a native->managed call and are generating a return buffer, + // we need to move all of the actual arguments over one and have the return value be the first argument (after the this pointer if applicable). + if (fReverseWithReturnBufferArg) + { + ++argOffset; + } + + if (fMarshalReturnValueFirst) + { + marshalType = DoMarshalReturnValue(msig, + pParamTokenArray, + nlType, + nlFlags, + 0, + pss, + fThisCall, + argOffset, + dwStubFlags, + pMD, + nativeStackSize, + fStubNeedsCOM, + 0 + DEBUG_ARG(pSigDesc->m_pDebugName) + DEBUG_ARG(pSigDesc->m_pDebugClassName) + ); + + 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. + MethodTable *pRetMT = msig.GetRetTypeHandleThrowing().AsMethodTable(); + if (IsUnmanagedValueTypeReturnedByRef(pRetMT->GetNativeSize())) + { + nativeStackSize += sizeof(LPVOID); + } + } + } + while (argidx <= numArgs) { #ifdef FEATURE_COMINTEROP diff --git a/src/vm/dllimport.h b/src/vm/dllimport.h index b88d339a84..a6967ebfb4 100644 --- a/src/vm/dllimport.h +++ b/src/vm/dllimport.h @@ -494,6 +494,10 @@ public: void SetInteropParamExceptionInfo(UINT resID, UINT paramIdx); bool HasInteropParamExceptionInfo(); + bool TargetHasThis() + { + return m_targetHasThis == TRUE; + } void ClearCode(); @@ -556,6 +560,7 @@ protected: BOOL m_fHasCleanupCode; BOOL m_fHasExceptionCleanupCode; BOOL m_fCleanupWorkListIsSetup; + BOOL m_targetHasThis; DWORD m_dwThreadLocalNum; // managed-to-native only DWORD m_dwArgMarshalIndexLocalNum; DWORD m_dwCleanupWorkListLocalNum; diff --git a/src/vm/ilmarshalers.h b/src/vm/ilmarshalers.h index c892ae66a9..87d81504e7 100644 --- a/src/vm/ilmarshalers.h +++ b/src/vm/ilmarshalers.h @@ -583,7 +583,10 @@ public: bool byrefNativeReturn = false; CorElementType typ = ELEMENT_TYPE_VOID; UINT32 nativeSize = 0; - + bool nativeMethodIsMemberFunction = (m_pslNDirect->TargetHasThis() && IsCLRToNative(m_dwMarshalFlags)) + || (m_pslNDirect->HasThis() && !IsCLRToNative(m_dwMarshalFlags)) + || ((CorInfoCallConv)m_pslNDirect->GetStubTargetCallingConv() == CORINFO_CALLCONV_THISCALL); + // we need to convert value type return types to primitives as // JIT does not inline P/Invoke calls that return structures if (nativeType.IsValueClass()) @@ -599,33 +602,56 @@ public: nativeSize = wNativeSize; } -#if defined(_TARGET_X86_) +#if defined(_TARGET_X86_) || (defined(_TARGET_AMD64_) && defined(_WIN32)) // JIT32 and JIT64 (which is only used on the Windows Desktop CLR) has a problem generating // code for the pinvoke ILStubs which do a return using a struct type. Therefore, we // change the signature of calli to return void and make the return buffer as first argument. - // for X86 and AMD64-Windows we bash the return type from struct to U1, U2, U4 or U8 + // For Windows AMD64 and x86, we need to use a return buffer for native member functions returning structures. + // for X86 Windows non-member functions we bash the return type from struct to U1, U2, U4 or U8 // and use byrefNativeReturn for all other structs. // for UNIX_X86_ABI, we always need a return buffer argument for any size of structs. - switch (nativeSize) +#if defined(_WIN32) + if (nativeMethodIsMemberFunction) { + byrefNativeReturn = true; + } + else +#endif // _WIN32 + { +#ifndef _TARGET_AMD64_ + switch (nativeSize) + { #ifndef UNIX_X86_ABI - case 1: typ = ELEMENT_TYPE_U1; break; - case 2: typ = ELEMENT_TYPE_U2; break; - case 4: typ = ELEMENT_TYPE_U4; break; - case 8: typ = ELEMENT_TYPE_U8; break; -#endif - default: byrefNativeReturn = true; break; + case 1: typ = ELEMENT_TYPE_U1; break; + case 2: typ = ELEMENT_TYPE_U2; break; + case 4: typ = ELEMENT_TYPE_U4; break; + case 8: typ = ELEMENT_TYPE_U8; break; +#endif // UNIX_X86_ABI + default: byrefNativeReturn = true; break; + } +#endif // _TARGET_AMD64_ } -#endif +#endif // defined(_TARGET_X86_) || (defined(_TARGET_AMD64_) && defined(_WIN32)) } - if (IsHresultSwap(dwMarshalFlags) || (byrefNativeReturn && IsCLRToNative(dwMarshalFlags))) + if (IsHresultSwap(dwMarshalFlags) || (byrefNativeReturn && (IsCLRToNative(m_dwMarshalFlags) || nativeMethodIsMemberFunction))) { LocalDesc extraParamType = nativeType; extraParamType.MakeByRef(); m_pcsMarshal->SetStubTargetArgType(&extraParamType, false); + if (byrefNativeReturn && !IsCLRToNative(m_dwMarshalFlags)) + { + // If doing a native->managed call and returning a structure by-ref, + // the native signature has an extra param for the struct return + // than the managed signature. Adjust the target stack delta to account this extra + // parameter. + m_pslNDirect->AdjustTargetStackDeltaForExtraParam(); + // We also need to account for the lack of a return value in the native signature. + // To do this, we adjust the stack delta again for the return parameter. + m_pslNDirect->AdjustTargetStackDeltaForExtraParam(); + } if (IsHresultSwap(dwMarshalFlags)) { @@ -742,6 +768,10 @@ public: m_nativeHome.EmitCopyToByrefArgWithNullCheck(m_pcsUnmarshal, &nativeType, argidx); m_pcsUnmarshal->EmitLDC(S_OK); } + else if (byrefNativeReturn && nativeMethodIsMemberFunction) + { + m_nativeHome.EmitCopyToByrefArg(m_pcsUnmarshal, &nativeType, argidx); + } else { if (typ != ELEMENT_TYPE_VOID) diff --git a/src/vm/stubgen.cpp b/src/vm/stubgen.cpp index 67a6ca03e9..78c6831b71 100644 --- a/src/vm/stubgen.cpp +++ b/src/vm/stubgen.cpp @@ -1530,7 +1530,7 @@ void ILCodeStream::EmitPOP() void ILCodeStream::EmitRET() { WRAPPER_NO_CONTRACT; - INT16 iStackDelta = m_pOwner->m_StubHasVoidReturnType ? 0 : -1; + INT16 iStackDelta = m_pOwner->ReturnOpcodePopsStack() ? -1 : 0; Emit(CEE_RET, iStackDelta, 0); } void ILCodeStream::EmitSHR_UN() @@ -1684,11 +1684,6 @@ void ILCodeStream::EmitCALL(BinderMethodID id, int numInArgs, int numRetArgs) EmitCALL(GetToken(MscorlibBinder::GetMethod(id)), numInArgs, numRetArgs); } - - - - - void ILStubLinker::SetHasThis (bool fHasThis) { LIMITED_METHOD_CONTRACT; @@ -2156,14 +2151,15 @@ static BOOL SigHasVoidReturnType(const Signature &signature) ILStubLinker::ILStubLinker(Module* pStubSigModule, const Signature &signature, SigTypeContext *pTypeContext, MethodDesc *pMD, - BOOL fTargetHasThis, BOOL fStubHasThis, BOOL fIsNDirectStub) : + BOOL fTargetHasThis, BOOL fStubHasThis, BOOL fIsNDirectStub, BOOL fIsReverseStub) : m_pCodeStreamList(NULL), m_stubSig(signature), m_pTypeContext(pTypeContext), m_pCode(NULL), m_pStubSigModule(pStubSigModule), m_pLabelList(NULL), - m_StubHasVoidReturnType(0), + m_StubHasVoidReturnType(FALSE), + m_fIsReverseStub(fIsReverseStub), m_iTargetStackDelta(0), m_cbCurrentCompressedSigLen(1), m_nLocals(0), @@ -2181,7 +2177,9 @@ ILStubLinker::ILStubLinker(Module* pStubSigModule, const Signature &signature, S m_managedSigPtr = signature.CreateSigPointer(); if (!signature.IsEmpty()) { + // Until told otherwise, assume that the stub has the same return type as the signature. m_StubHasVoidReturnType = SigHasVoidReturnType(signature); + m_StubTargetHasVoidReturnType = m_StubHasVoidReturnType; // // Get the stub's calling convention. Set m_fHasThis to match @@ -2477,10 +2475,13 @@ void ILStubLinker::SetStubTargetReturnType(LocalDesc* pLoc) m_nativeFnSigBuilder.SetReturnType(pLoc); - if ((1 != pLoc->cbType) || (ELEMENT_TYPE_VOID != pLoc->ElementType[0])) + // Update check for if a stub has a void return type based on the provided return type. + m_StubTargetHasVoidReturnType = ((1 == pLoc->cbType) && (ELEMENT_TYPE_VOID == pLoc->ElementType[0])) ? TRUE : FALSE; + if (!m_StubTargetHasVoidReturnType) { m_iTargetStackDelta++; } + } DWORD ILStubLinker::SetStubTargetArgType(CorElementType typ, bool fConsumeStubArg /*= true*/) diff --git a/src/vm/stubgen.h b/src/vm/stubgen.h index 044029b0af..73d72708a7 100644 --- a/src/vm/stubgen.h +++ b/src/vm/stubgen.h @@ -110,11 +110,11 @@ struct LocalDesc bool lastElementTypeIsValueType = false; - if (ElementType[0] == ELEMENT_TYPE_VALUETYPE) + if (ElementType[cbType - 1] == ELEMENT_TYPE_VALUETYPE) { lastElementTypeIsValueType = true; } - else if ((ElementType[0] == ELEMENT_TYPE_INTERNAL) && + else if ((ElementType[cbType - 1] == ELEMENT_TYPE_INTERNAL) && (InternalToken.IsNativeValueType() || InternalToken.GetMethodTable()->IsValueType())) { @@ -378,7 +378,7 @@ class ILStubLinker public: ILStubLinker(Module* pModule, const Signature &signature, SigTypeContext *pTypeContext, MethodDesc *pMD, - BOOL fTargetHasThis, BOOL fStubHasThis, BOOL fIsNDirectStub = FALSE); + BOOL fTargetHasThis, BOOL fStubHasThis, BOOL fIsNDirectStub = FALSE, BOOL fIsReverseStub = FALSE); ~ILStubLinker(); void GenerateCode(BYTE* pbBuffer, size_t cbBufferSize); @@ -424,7 +424,6 @@ public: void GetStubReturnType(LocalDesc * pLoc); void GetStubReturnType(LocalDesc * pLoc, Module * pModule); CorCallingConvention GetStubTargetCallingConv(); - CorElementType GetStubTargetReturnElementType() { WRAPPER_NO_CONTRACT; return m_nativeFnSigBuilder.GetReturnElementType(); } @@ -504,12 +503,23 @@ protected: void SetStubTargetReturnType(LocalDesc* pLoc); void SetStubTargetCallingConv(CorCallingConvention uNativeCallingConv); + bool ReturnOpcodePopsStack() + { + if ((!m_fIsReverseStub && m_StubHasVoidReturnType) || (m_fIsReverseStub && m_StubTargetHasVoidReturnType)) + { + return false; + } + return true; + } + void TransformArgForJIT(LocalDesc *pLoc); Module * GetStubSigModule(); SigTypeContext *GetStubSigTypeContext(); BOOL m_StubHasVoidReturnType; + BOOL m_StubTargetHasVoidReturnType; + BOOL m_fIsReverseStub; INT m_iTargetStackDelta; DWORD m_cbCurrentCompressedSigLen; DWORD m_nLocals; |