summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/vm/dllimport.cpp179
-rw-r--r--src/vm/dllimport.h5
-rw-r--r--src/vm/ilmarshalers.h54
-rw-r--r--src/vm/stubgen.cpp19
-rw-r--r--src/vm/stubgen.h18
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;