* Enable return buffers on Windows ARM64 for struct-returning member functions. Fixes #23577.
* Update comment to match new condition.
* Enable byref return on all Windows (excluding arm64 HFA. Add more test cases for ThisCall.
* On x86, if we have a normalized return value in an instance method, get back the actual type so that we correctly marshal it via a return buffer.
* Fix param ordering.
* Clean up based on PR feedback.
* Fix accidental variable shadowing.
MarshalInfo Info(msig.GetModule(), msig.GetArgProps(), msig.GetSigTypeContext(), params[iParam],
MarshalInfo::MARSHAL_SCENARIO_COMINTEROP, (CorNativeLinkType)0, (CorNativeLinkFlags)0,
- TRUE, iParam, numArgs, BestFit, ThrowOnUnmappableChar, FALSE, pMD, TRUE
+ TRUE, iParam, numArgs, BestFit, ThrowOnUnmappableChar, FALSE, TRUE, pMD, TRUE
#ifdef _DEBUG
, pMD->m_pszDebugMethodName, pMD->m_pszDebugClassName, iParam
#endif
MarshalInfo info(fsig.GetModule(), fsig.GetArgProps(), fsig.GetSigTypeContext(), pFD->GetMemberDef(), MarshalInfo::MARSHAL_SCENARIO_COMINTEROP,
(CorNativeLinkType)0, (CorNativeLinkFlags)0,
- FALSE, 0, fsig.NumFixedArgs(), BestFit, ThrowOnUnmappableChar, FALSE, NULL, FALSE
+ FALSE, 0, fsig.NumFixedArgs(), BestFit, ThrowOnUnmappableChar, FALSE, TRUE, NULL, FALSE
#ifdef _DEBUG
, szDebugName, szDebugClassName, 0
#endif
MarshalInfo info(msig.GetModule(), msig.GetArgProps(), msig.GetSigTypeContext(), params[iArg],
WinRTType ? MarshalInfo::MARSHAL_SCENARIO_WINRT : MarshalInfo::MARSHAL_SCENARIO_COMINTEROP,
(CorNativeLinkType)0, (CorNativeLinkFlags)0,
- TRUE, iArg, numArgs, BestFit, ThrowOnUnmappableChar, FALSE, pMD, FALSE
+ TRUE, iArg, numArgs, BestFit, ThrowOnUnmappableChar, FALSE, TRUE, pMD, FALSE
#ifdef _DEBUG
, szDebugName, szDebugClassName, iArg
#endif
MarshalInfo info(msig.GetModule(), msig.GetReturnProps(), msig.GetSigTypeContext(), params[0],
WinRTType ? MarshalInfo::MARSHAL_SCENARIO_WINRT : MarshalInfo::MARSHAL_SCENARIO_COMINTEROP,
(CorNativeLinkType)0, (CorNativeLinkFlags)0,
- FALSE, 0, numArgs, BestFit, ThrowOnUnmappableChar, FALSE, pMD, FALSE
+ FALSE, 0, numArgs, BestFit, ThrowOnUnmappableChar, FALSE, TRUE, pMD, FALSE
#ifdef _DEBUG
,szDebugName, szDebugClassName, 0
#endif
MarshalInfo Info(msig.GetModule(), msig.GetArgProps(), msig.GetSigTypeContext(), paramDef, MarshalInfo::MARSHAL_SCENARIO_COMINTEROP,
(CorNativeLinkType)0, (CorNativeLinkFlags)0,
- TRUE, iParam, numArgs, BestFit, ThrowOnUnmappableChar, FALSE, pMD, TRUE
+ TRUE, iParam, numArgs, BestFit, ThrowOnUnmappableChar, FALSE, TRUE, pMD, TRUE
#ifdef _DEBUG
, pMD->m_pszDebugMethodName, pMD->m_pszDebugClassName, iParam
#endif
{
MarshalInfo Info(msig.GetModule(), msig.GetReturnProps(), msig.GetSigTypeContext(), returnParamDef, MarshalInfo::MARSHAL_SCENARIO_COMINTEROP,
(CorNativeLinkType)0, (CorNativeLinkFlags)0,
- FALSE, 0, numArgs, BestFit, ThrowOnUnmappableChar, FALSE, pMD, TRUE
+ FALSE, 0, numArgs, BestFit, ThrowOnUnmappableChar, FALSE, TRUE, pMD, TRUE
#ifdef _DEBUG
, pMD->m_pszDebugMethodName, pMD->m_pszDebugClassName, 0
#endif
CorNativeLinkFlags nlFlags,
UINT argidx, // this is used for reverse pinvoke hresult swapping
StubState* pss,
- BOOL fThisCall,
+ BOOL isInstanceMethod,
int argOffset,
DWORD dwStubFlags,
MethodDesc *pMD,
SF_IsBestFit(dwStubFlags),
SF_IsThrowOnUnmappableChar(dwStubFlags),
TRUE,
+ isInstanceMethod,
pMD,
TRUE
DEBUG_ARG(pDebugName)
BOOL fMarshalReturnValueFirst = FALSE;
BOOL fReverseWithReturnBufferArg = FALSE;
+ bool isInstanceMethod = fStubNeedsCOM || fThisCall;
// 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.
// 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_)
// 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_)
+#elif defined(_TARGET_AMD64_) || defined (_TARGET_ARM64_)
fMarshalReturnValueFirst = isInstanceMethod && isReturnTypeValueType;
#endif // defined(_TARGET_X86_) || defined(_TARGET_ARM_)
#ifdef _WIN32
SF_IsBestFit(dwStubFlags),
SF_IsThrowOnUnmappableChar(dwStubFlags),
TRUE,
+ isInstanceMethod ? TRUE : FALSE,
pMD,
TRUE
DEBUG_ARG(pSigDesc->m_pDebugName)
nlFlags,
0,
pss,
- fThisCall,
+ isInstanceMethod,
argOffset,
dwStubFlags,
pMD,
nlFlags,
argidx,
pss,
- fThisCall,
+ isInstanceMethod,
argOffset,
dwStubFlags,
pMD,
nativeSize = wNativeSize;
}
-#if defined(_TARGET_X86_) || (defined(_TARGET_AMD64_) && defined(_WIN32))
+#if defined(PLATFORM_WINDOWS)
// 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 Windows AMD64 and x86, we need to use a return buffer for native member functions returning structures.
+ // For Windows, we need to use a return buffer for native member functions returning structures.
+ // On Windows arm we need to respect HFAs and not use a return buffer if the return type is an HFA
// 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.
-#if defined(_WIN32)
if (nativeMethodIsMemberFunction)
{
+#ifdef _TARGET_ARM_
+ byrefNativeReturn = !nativeType.InternalToken.GetMethodTable()->IsNativeHFA();
+#else
byrefNativeReturn = true;
+#endif
}
else
-#endif // _WIN32
{
-#ifndef _TARGET_AMD64_
+#ifdef _TARGET_X86_
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 // UNIX_X86_ABI
default: byrefNativeReturn = true; break;
}
-#endif // _TARGET_AMD64_
+#endif // _TARGET_X86_
}
-#endif // defined(_TARGET_X86_) || (defined(_TARGET_AMD64_) && defined(_WIN32))
+#endif // defined(PLATFORM_WINDOWS)
+
+ // for UNIX_X86_ABI, we always need a return buffer argument for any size of structs.
+#ifdef UNIX_X86_ABI
+ byrefNativeReturn = true;
+#endif
}
if (IsHresultSwap(dwMarshalFlags) || (byrefNativeReturn && (IsCLRToNative(m_dwMarshalFlags) || nativeMethodIsMemberFunction)))
BOOL BestFit,
BOOL ThrowOnUnmappableChar,
BOOL fEmitsIL,
+ BOOL onInstanceMethod,
MethodDesc* pMD,
BOOL fLoadCustomMarshal
#ifdef _DEBUG
}
nativeType = ParamInfo.m_NativeType;
+
corElemType = sig.PeekElemTypeNormalized(pModule, pTypeContext);
- mtype = corElemType;
+ mtype = corElemType;
#ifdef FEATURE_COMINTEROP
if (IsWinRTScenario() && nativeType != NATIVE_TYPE_DEFAULT)
// "un-normalized" signature type. It has to be verified that all the value types
// that have been normalized away have default marshaling or MarshalAs(Struct).
// In addition, the nativeType must be updated with the type of the real primitive inside.
- //
- VerifyAndAdjustNormalizedType(pModule, sig, pTypeContext, &mtype, &nativeType);
+ // We don't normalize on return values of member functions since struct return values need to be treated as structures.
+ if (isParam || !onInstanceMethod)
+ {
+ VerifyAndAdjustNormalizedType(pModule, sig, pTypeContext, &mtype, &nativeType);
+ }
+ else
+ {
+ mtype = sig.PeekElemTypeClosed(pModule, pTypeContext);
+ }
#endif // _TARGET_X86_
// (returning small value types by value in registers) is already done in JIT64.
if ( !m_byref // Permit register-sized structs as return values
&& !isParam
+ && !onInstanceMethod
&& CorIsPrimitiveType(m_pMT->GetInternalCorElementType())
&& !IsUnmanagedValueTypeReturnedByRef(nativeSize)
&& managedSize <= sizeof(void*)
BOOL BestFit,
BOOL ThrowOnUnmappableChar,
BOOL fEmitsIL,
+ BOOL onInstanceMethod,
MethodDesc* pMD = NULL,
BOOL fUseCustomMarshal = TRUE
#ifdef _DEBUG
float height;
};
+struct Width
+{
+ float width;
+};
+
+struct IntWrapper
+{
+ int i;
+};
+
class C
{
int dummy = 0xcccccccc;
{
return {width, height};
}
+
+ virtual Width GetWidth()
+ {
+ return {width};
+ }
+
+ virtual IntWrapper GetHeightAsInt()
+ {
+ return {(int)height};
+ }
};
public struct VtableLayout
{
public IntPtr getSize;
+ public IntPtr getWidth;
+ public IntPtr getHeightAsInt;
}
public VtableLayout* vtable;
private int c;
- private float width;
- private float height;
+ public readonly float width;
+ public readonly float height;
}
public struct SizeF
public float height;
}
+ public struct Width
+ {
+ public float width;
+ }
+
+ public struct IntWrapper
+ {
+ public int i;
+ }
+
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
public delegate SizeF GetSizeFn(C* c);
+ [UnmanagedFunctionPointer(CallingConvention.ThisCall)]
+ public delegate Width GetWidthFn(C* c);
+ [UnmanagedFunctionPointer(CallingConvention.ThisCall)]
+ public delegate IntWrapper GetHeightAsIntFn(C* c);
[DllImport(nameof(ThisCallNative))]
public static extern C* CreateInstanceOfC(float width, float height);
float width = 1.0f;
float height = 2.0f;
ThisCallNative.C* instance = ThisCallNative.CreateInstanceOfC(width, height);
- ThisCallNative.GetSizeFn callback = Marshal.GetDelegateForFunctionPointer<ThisCallNative.GetSizeFn>(instance->vtable->getSize);
-
- ThisCallNative.SizeF result = callback(instance);
-
- Assert.AreEqual(width, result.width);
- Assert.AreEqual(height, result.height);
+ Test8ByteHFA(instance);
+ Test4ByteHFA(instance);
+ Test4ByteNonHFA(instance);
}
catch (System.Exception ex)
{
}
return 100;
}
+
+ private static unsafe void Test8ByteHFA(ThisCallNative.C* instance)
+ {
+ ThisCallNative.GetSizeFn callback = Marshal.GetDelegateForFunctionPointer<ThisCallNative.GetSizeFn>(instance->vtable->getSize);
+
+ ThisCallNative.SizeF result = callback(instance);
+
+ Assert.AreEqual(instance->width, result.width);
+ Assert.AreEqual(instance->height, result.height);
+ }
+
+ private static unsafe void Test4ByteHFA(ThisCallNative.C* instance)
+ {
+ ThisCallNative.GetWidthFn callback = Marshal.GetDelegateForFunctionPointer<ThisCallNative.GetWidthFn>(instance->vtable->getWidth);
+
+ ThisCallNative.Width result = callback(instance);
+
+ Assert.AreEqual(instance->width, result.width);
+ }
+
+ private static unsafe void Test4ByteNonHFA(ThisCallNative.C* instance)
+ {
+ ThisCallNative.GetHeightAsIntFn callback = Marshal.GetDelegateForFunctionPointer<ThisCallNative.GetHeightAsIntFn>(instance->vtable->getHeightAsInt);
+
+ ThisCallNative.IntWrapper result = callback(instance);
+
+ Assert.AreEqual((int)instance->height, result.i);
+ }
}