From c17d78676b13ee45891c8acbdc237602cfdeed88 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 7 Jul 2020 12:01:37 -0700 Subject: [PATCH 01/73] Turn off interop-level system to enable thiscall handling --- src/coreclr/src/vm/dllimport.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/src/vm/dllimport.cpp b/src/coreclr/src/vm/dllimport.cpp index 0850877c8089cf..704fb201a4d7a7 100644 --- a/src/coreclr/src/vm/dllimport.cpp +++ b/src/coreclr/src/vm/dllimport.cpp @@ -3473,7 +3473,7 @@ static void CreateNDirectStubWorker(StubState* pss, // Only consider ThisCall methods to be instance methods. // Techinically COM methods are also instance methods, but we don't want to change the behavior of the built-in // COM abi work because there are many users that rely on the current behavior (for example WPF). - bool isInstanceMethod = fThisCall; + bool isInstanceMethod = 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. From 8eecd353d01cf7a988936965a54e9006629da72d Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 7 Jul 2020 12:05:26 -0700 Subject: [PATCH 02/73] Generate a return buffer for instance method callis. --- src/coreclr/src/jit/gentree.h | 2 ++ src/coreclr/src/jit/importer.cpp | 15 ++++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/coreclr/src/jit/gentree.h b/src/coreclr/src/jit/gentree.h index 2a46ef51088529..6ea1387003bd87 100644 --- a/src/coreclr/src/jit/gentree.h +++ b/src/coreclr/src/jit/gentree.h @@ -4159,6 +4159,8 @@ struct GenTreeCall final : public GenTree #define GTF_CALL_M_ALLOC_SIDE_EFFECTS 0x00400000 // GT_CALL -- this is a call to an allocator with side effects #define GTF_CALL_M_SUPPRESS_GC_TRANSITION 0x00800000 // GT_CALL -- suppress the GC transition (i.e. during a pinvoke) but a separate GC safe point is required. #define GTF_CALL_M_EXP_RUNTIME_LOOKUP 0x01000000 // GT_CALL -- this call needs to be tranformed into CFG for the dynamic dictionary expansion feature. +#define GTF_CALL_M_UNMGD_INST_CALL 0x02000000 // GT_CALL -- this call is a call to an unmanaged instance method + // (only for GTF_CALL_UNMANAGED) // clang-format on diff --git a/src/coreclr/src/jit/importer.cpp b/src/coreclr/src/jit/importer.cpp index ee8a326e52e9d4..846a9ac504a277 100644 --- a/src/coreclr/src/jit/importer.cpp +++ b/src/coreclr/src/jit/importer.cpp @@ -6758,6 +6758,7 @@ void Compiler::impCheckForPInvokeCall( if (unmanagedCallConv == CORINFO_UNMANAGED_CALLCONV_THISCALL) { call->gtCallMoreFlags |= GTF_CALL_M_UNMGD_THISCALL; + call->gtCallMoreFlags |= GTF_CALL_M_UNMGD_INST_CALL; } } @@ -9022,7 +9023,19 @@ GenTree* Compiler::impFixupCallStructReturn(GenTreeCall* call, CORINFO_CLASS_HAN // and we change the return type on those calls here. // structPassingKind howToReturnStruct; - var_types returnType = getReturnTypeForStruct(retClsHnd, &howToReturnStruct); + var_types returnType; + +#if defined(TARGET_WINDOWS) && !defined(TARGET_ARM) + if (call->gtCallMoreFlags & GTF_CALL_M_UNMGD_INST_CALL) + { + howToReturnStruct = SPK_ByReference; + returnType = TYP_UNKNOWN; + } + else +#endif + { + returnType = getReturnTypeForStruct(retClsHnd, &howToReturnStruct); + } if (howToReturnStruct == SPK_ByReference) { From c06a0402d1dfb24f889ed6555bdc8257692a2c22 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 7 Jul 2020 14:52:04 -0700 Subject: [PATCH 03/73] On Windows non-arm32, unmanaged instance methods have their hidden retbuf arg after the `this` parameter, not before. --- src/coreclr/src/jit/importer.cpp | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/coreclr/src/jit/importer.cpp b/src/coreclr/src/jit/importer.cpp index 846a9ac504a277..71c731aa562daa 100644 --- a/src/coreclr/src/jit/importer.cpp +++ b/src/coreclr/src/jit/importer.cpp @@ -1184,12 +1184,23 @@ GenTree* Compiler::impAssignStructPtr(GenTree* destAddr, if (src->gtOper == GT_CALL) { - if (src->AsCall()->TreatAsHasRetBufArg(this)) + GenTreeCall* srcCall = src->AsCall(); + if (srcCall->TreatAsHasRetBufArg(this)) { // Case of call returning a struct via hidden retbuf arg - // insert the return value buffer into the argument list as first byref parameter - src->AsCall()->gtCallArgs = gtPrependNewCallArg(destAddr, src->AsCall()->gtCallArgs); +#if defined(TARGET_WINDOWS) && !defined(TARGET_ARM) + // Unmanaged instance methods on Windows need the retbuf arg after the first (this) parameter + if ((srcCall->gtFlags & GTF_CALL_UNMANAGED) && (srcCall->gtCallMoreFlags & GTF_CALL_M_UNMGD_INST_CALL)) + { + GenTreeCall::Use* thisArg = gtInsertNewCallArgAfter(destAddr, srcCall->gtCallArgs); + } + else +#endif + { + // insert the return value buffer into the argument list as first byref parameter + srcCall->gtCallArgs = gtPrependNewCallArg(destAddr, srcCall->gtCallArgs); + } // now returns void, not a struct src->gtType = TYP_VOID; @@ -1201,7 +1212,7 @@ GenTree* Compiler::impAssignStructPtr(GenTree* destAddr, { // Case of call returning a struct in one or more registers. - var_types returnType = (var_types)src->AsCall()->gtReturnType; + var_types returnType = (var_types)srcCall->gtReturnType; if (compDoOldStructRetyping()) { From 9448965d05c8cb0333c0c3d5ddee64d9befa8ef6 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 7 Jul 2020 17:18:16 -0700 Subject: [PATCH 04/73] Teach the JIT to generate a return buffer for reverse P/Invokes for thiscall on required platforms. Still need to reorder the emit so the arguments are emitted correctly. --- src/coreclr/src/jit/compiler.cpp | 3 +++ src/coreclr/src/jit/importer.cpp | 6 ++++++ src/coreclr/src/vm/dllimport.cpp | 7 +++++++ 3 files changed, 16 insertions(+) diff --git a/src/coreclr/src/jit/compiler.cpp b/src/coreclr/src/jit/compiler.cpp index 11ffd258c09c80..ebac3bd2062b17 100644 --- a/src/coreclr/src/jit/compiler.cpp +++ b/src/coreclr/src/jit/compiler.cpp @@ -5957,6 +5957,9 @@ int Compiler::compCompileHelper(CORINFO_MODULE_HANDLE classPtr, case CORINFO_CALLCONV_NATIVEVARARG: info.compIsVarArgs = true; break; + case CORINFO_CALLCONV_C: + case CORINFO_CALLCONV_STDCALL: + case CORINFO_CALLCONV_THISCALL: case CORINFO_CALLCONV_DEFAULT: info.compIsVarArgs = false; break; diff --git a/src/coreclr/src/jit/importer.cpp b/src/coreclr/src/jit/importer.cpp index 71c731aa562daa..fbc90fff424c91 100644 --- a/src/coreclr/src/jit/importer.cpp +++ b/src/coreclr/src/jit/importer.cpp @@ -8872,6 +8872,12 @@ bool Compiler::impMethodInfo_hasRetBuffArg(CORINFO_METHOD_INFO* methInfo) if ((corType == CORINFO_TYPE_VALUECLASS) || (corType == CORINFO_TYPE_REFANY)) { // We have some kind of STRUCT being returned +#if defined(TARGET_WINDOWS) && !defined(TARGET_ARM) + if (methInfo->args.getCallConv() == CORINFO_CALLCONV_THISCALL) + { + return true; + } +#endif structPassingKind howToReturnStruct = SPK_Unknown; diff --git a/src/coreclr/src/vm/dllimport.cpp b/src/coreclr/src/vm/dllimport.cpp index 704fb201a4d7a7..77a036721b9567 100644 --- a/src/coreclr/src/vm/dllimport.cpp +++ b/src/coreclr/src/vm/dllimport.cpp @@ -1351,10 +1351,17 @@ class PInvoke_ILStubState : public ILStubState { STANDARD_VM_CONTRACT; +#if defined(TARGET_X86) && !defined(FEATURE_STUBS_AS_IL) + // x86 with non-IL stubs manually handles calling conventions + // for reverse P/Invokes with the x86 stub linker. + // Don't use the JIT calling convention support on reverse P/Invokes. if (SF_IsForwardStub(dwStubFlags)) { m_slIL.SetCallingConvention(unmgdCallConv, SF_IsVarArgStub(dwStubFlags)); } +#else + m_slIL.SetCallingConvention(unmgdCallConv, SF_IsVarArgStub(dwStubFlags)); +#endif } private: From 7a26d400c357e71769b590e48e4af8cda959466a Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 8 Jul 2020 11:17:09 -0700 Subject: [PATCH 05/73] Emit the native `this` argument before the return buffer when needed. --- src/coreclr/src/jit/compiler.cpp | 5 ++++ src/coreclr/src/jit/compiler.h | 7 ++++- src/coreclr/src/jit/importer.cpp | 2 +- src/coreclr/src/jit/lclvars.cpp | 44 +++++++++++++++++++++++++++++--- 4 files changed, 53 insertions(+), 5 deletions(-) diff --git a/src/coreclr/src/jit/compiler.cpp b/src/coreclr/src/jit/compiler.cpp index ebac3bd2062b17..867505ff520294 100644 --- a/src/coreclr/src/jit/compiler.cpp +++ b/src/coreclr/src/jit/compiler.cpp @@ -1972,6 +1972,11 @@ unsigned Compiler::compGetTypeSize(CorInfoType cit, CORINFO_CLASS_HANDLE clsHnd) return sigSize; } +bool Compiler::compMethodIsNativeInstanceMethod(CORINFO_METHOD_INFO* mthInfo) +{ + return mthInfo->args.getCallConv() == CORINFO_CALLCONV_THISCALL; +} + #ifdef DEBUG static bool DidComponentUnitTests = false; diff --git a/src/coreclr/src/jit/compiler.h b/src/coreclr/src/jit/compiler.h index be64fa8c3e6b4a..e512fd78079547 100644 --- a/src/coreclr/src/jit/compiler.h +++ b/src/coreclr/src/jit/compiler.h @@ -3291,7 +3291,7 @@ class Compiler void lvaInitArgs(InitVarDscInfo* varDscInfo); void lvaInitThisPtr(InitVarDscInfo* varDscInfo); void lvaInitRetBuffArg(InitVarDscInfo* varDscInfo); - void lvaInitUserArgs(InitVarDscInfo* varDscInfo); + void lvaInitUserArgs(InitVarDscInfo* varDscInfo, unsigned skipArgs, unsigned takeArgs); void lvaInitGenericsCtxt(InitVarDscInfo* varDscInfo); void lvaInitVarArgsHandle(InitVarDscInfo* varDscInfo); @@ -9341,6 +9341,11 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX // size of the type these describe. unsigned compGetTypeSize(CorInfoType cit, CORINFO_CLASS_HANDLE clsHnd); + // Calculates if this method's entry point should have the same calling + // convention as an unmanaged instance method variant of the standard calling convention. + // (only used on applicable platforms) + bool compMethodIsNativeInstanceMethod(CORINFO_METHOD_INFO* mthInfo); + #ifdef DEBUG // Components used by the compiler may write unit test suites, and // have them run within this method. They will be run only once per process, and only diff --git a/src/coreclr/src/jit/importer.cpp b/src/coreclr/src/jit/importer.cpp index fbc90fff424c91..e5c8ae4fecfb50 100644 --- a/src/coreclr/src/jit/importer.cpp +++ b/src/coreclr/src/jit/importer.cpp @@ -8873,7 +8873,7 @@ bool Compiler::impMethodInfo_hasRetBuffArg(CORINFO_METHOD_INFO* methInfo) { // We have some kind of STRUCT being returned #if defined(TARGET_WINDOWS) && !defined(TARGET_ARM) - if (methInfo->args.getCallConv() == CORINFO_CALLCONV_THISCALL) + if (compMethodIsNativeInstanceMethod(methInfo)) { return true; } diff --git a/src/coreclr/src/jit/lclvars.cpp b/src/coreclr/src/jit/lclvars.cpp index f8757328e85130..4ba8bd79b2b384 100644 --- a/src/coreclr/src/jit/lclvars.cpp +++ b/src/coreclr/src/jit/lclvars.cpp @@ -349,6 +349,18 @@ void Compiler::lvaInitArgs(InitVarDscInfo* varDscInfo) /* Is there a "this" pointer ? */ lvaInitThisPtr(varDscInfo); + unsigned numUserArgsToSkip = 0; + unsigned numUserArgs = info.compMethodInfo->args.numArgs; +#if defined(TARGET_WINDOWS) && !defined(TARGET_ARM) + if (compMethodIsNativeInstanceMethod(info.compMethodInfo)) + { + assert(numUserArgs >= 1); + lvaInitUserArgs(varDscInfo, 0, 1); + numUserArgsToSkip++; + numUserArgs--; + } +#endif + /* If we have a hidden return-buffer parameter, that comes here */ lvaInitRetBuffArg(varDscInfo); @@ -366,7 +378,7 @@ void Compiler::lvaInitArgs(InitVarDscInfo* varDscInfo) //------------------------------------------------------------------------- // Now walk the function signature for the explicit user arguments //------------------------------------------------------------------------- - lvaInitUserArgs(varDscInfo); + lvaInitUserArgs(varDscInfo, numUserArgsToSkip, numUserArgs); #if !USER_ARGS_COME_LAST //@GENERICS: final instantiation-info argument for shared generic methods @@ -557,7 +569,7 @@ void Compiler::lvaInitRetBuffArg(InitVarDscInfo* varDscInfo) } /*****************************************************************************/ -void Compiler::lvaInitUserArgs(InitVarDscInfo* varDscInfo) +void Compiler::lvaInitUserArgs(InitVarDscInfo* varDscInfo, unsigned skipArgs, unsigned takeArgs) { //------------------------------------------------------------------------- // Walk the function signature for the explicit arguments @@ -575,11 +587,20 @@ void Compiler::lvaInitUserArgs(InitVarDscInfo* varDscInfo) const unsigned argSigLen = info.compMethodInfo->args.numArgs; + const int64_t numUserArgs = min(takeArgs, (argSigLen - (int64_t)skipArgs)); + + if (numUserArgs <= 0) + { + return; + } + #ifdef TARGET_ARM regMaskTP doubleAlignMask = RBM_NONE; #endif // TARGET_ARM - for (unsigned i = 0; i < argSigLen; + for (unsigned i = 0; i < skipArgs; i++, argLst = info.compCompHnd->getArgNext(argLst)); + + for (unsigned i = 0; i < numUserArgs; i++, varDscInfo->varNum++, varDscInfo->varDsc++, argLst = info.compCompHnd->getArgNext(argLst)) { LclVarDsc* varDsc = varDscInfo->varDsc; @@ -5163,6 +5184,20 @@ void Compiler::lvaAssignVirtualFrameOffsetsToArgs() lclNum++; } + unsigned userArgsToSkip = 0; +#if defined(TARGET_WINDOWS) && !defined(TARGET_ARM) + if (compMethodIsNativeInstanceMethod(info.compMethodInfo)) + { + noway_assert(lvaTable[lclNum].lvIsRegArg); +#ifndef TARGET_X86 + argOffs = + lvaAssignVirtualFrameOffsetToArg(lclNum, REGSIZE_BYTES, argOffs); +#endif // TARGET_X86 + lclNum++; + userArgsToSkip++; + } +#endif + /* if we have a hidden buffer parameter, that comes here */ if (info.compRetBuffArg != BAD_VAR_NUM) @@ -5196,6 +5231,9 @@ void Compiler::lvaAssignVirtualFrameOffsetsToArgs() CORINFO_ARG_LIST_HANDLE argLst = info.compMethodInfo->args.args; unsigned argSigLen = info.compMethodInfo->args.numArgs; + assert(userArgsToSkip <= argSigLen); + argSigLen -= userArgsToSkip; + for(unsigned i = 0; i < userArgsToSkip; i++, argLst = info.compCompHnd->getArgNext(argLst)); #ifdef TARGET_ARM // From a75f746008f20863b4369a3b400237ddffa20bb5 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 8 Jul 2020 11:34:15 -0700 Subject: [PATCH 06/73] Remove isInstanceMethod support in the interop subsystem now that the JIT can handle it. --- src/coreclr/src/vm/clrtocomcall.cpp | 2 +- src/coreclr/src/vm/comtoclrcall.cpp | 2 +- src/coreclr/src/vm/dispatchinfo.cpp | 4 +-- src/coreclr/src/vm/dllimport.cpp | 51 +-------------------------- src/coreclr/src/vm/fieldmarshaler.cpp | 1 - src/coreclr/src/vm/ilmarshalers.h | 44 ++++++----------------- src/coreclr/src/vm/mlinfo.cpp | 14 ++------ src/coreclr/src/vm/mlinfo.h | 5 +-- 8 files changed, 19 insertions(+), 104 deletions(-) diff --git a/src/coreclr/src/vm/clrtocomcall.cpp b/src/coreclr/src/vm/clrtocomcall.cpp index 1e31736be3f203..6e53c04212fb1b 100644 --- a/src/coreclr/src/vm/clrtocomcall.cpp +++ b/src/coreclr/src/vm/clrtocomcall.cpp @@ -308,7 +308,7 @@ I4ARRAYREF SetUpWrapperInfo(MethodDesc *pMD) MarshalInfo Info(msig.GetModule(), msig.GetArgProps(), msig.GetSigTypeContext(), params[iParam], MarshalInfo::MARSHAL_SCENARIO_COMINTEROP, (CorNativeLinkType)0, (CorNativeLinkFlags)0, - TRUE, iParam, numArgs, BestFit, ThrowOnUnmappableChar, FALSE, TRUE, pMD, TRUE + TRUE, iParam, numArgs, BestFit, ThrowOnUnmappableChar, FALSE, pMD, TRUE #ifdef _DEBUG , pMD->m_pszDebugMethodName, pMD->m_pszDebugClassName, iParam #endif diff --git a/src/coreclr/src/vm/comtoclrcall.cpp b/src/coreclr/src/vm/comtoclrcall.cpp index dc448070e3a145..9d6fbc688d6c76 100644 --- a/src/coreclr/src/vm/comtoclrcall.cpp +++ b/src/coreclr/src/vm/comtoclrcall.cpp @@ -1177,7 +1177,7 @@ void ComCallMethodDesc::InitNativeInfo() MarshalInfo info(msig.GetModule(), msig.GetReturnProps(), msig.GetSigTypeContext(), params[0], MarshalInfo::MARSHAL_SCENARIO_COMINTEROP, (CorNativeLinkType)0, (CorNativeLinkFlags)0, - FALSE, 0, numArgs, BestFit, ThrowOnUnmappableChar, FALSE, TRUE, pMD, FALSE + FALSE, 0, numArgs, BestFit, ThrowOnUnmappableChar, FALSE, pMD, FALSE #ifdef _DEBUG ,szDebugName, szDebugClassName, 0 #endif diff --git a/src/coreclr/src/vm/dispatchinfo.cpp b/src/coreclr/src/vm/dispatchinfo.cpp index dca9108f1d8532..f6566089756f41 100644 --- a/src/coreclr/src/vm/dispatchinfo.cpp +++ b/src/coreclr/src/vm/dispatchinfo.cpp @@ -903,7 +903,7 @@ void DispatchMemberInfo::SetUpMethodMarshalerInfo(MethodDesc *pMD, BOOL bReturnV MarshalInfo Info(msig.GetModule(), msig.GetArgProps(), msig.GetSigTypeContext(), paramDef, MarshalInfo::MARSHAL_SCENARIO_COMINTEROP, (CorNativeLinkType)0, (CorNativeLinkFlags)0, - TRUE, iParam, numArgs, BestFit, ThrowOnUnmappableChar, FALSE, TRUE, pMD, TRUE + TRUE, iParam, numArgs, BestFit, ThrowOnUnmappableChar, FALSE, pMD, TRUE #ifdef _DEBUG , pMD->m_pszDebugMethodName, pMD->m_pszDebugClassName, iParam #endif @@ -940,7 +940,7 @@ void DispatchMemberInfo::SetUpMethodMarshalerInfo(MethodDesc *pMD, BOOL bReturnV { MarshalInfo Info(msig.GetModule(), msig.GetReturnProps(), msig.GetSigTypeContext(), returnParamDef, MarshalInfo::MARSHAL_SCENARIO_COMINTEROP, (CorNativeLinkType)0, (CorNativeLinkFlags)0, - FALSE, 0, numArgs, BestFit, ThrowOnUnmappableChar, FALSE, TRUE, pMD, TRUE + FALSE, 0, numArgs, BestFit, ThrowOnUnmappableChar, FALSE, pMD, TRUE #ifdef _DEBUG , pMD->m_pszDebugMethodName, pMD->m_pszDebugClassName, 0 #endif diff --git a/src/coreclr/src/vm/dllimport.cpp b/src/coreclr/src/vm/dllimport.cpp index 77a036721b9567..15b69f553afc55 100644 --- a/src/coreclr/src/vm/dllimport.cpp +++ b/src/coreclr/src/vm/dllimport.cpp @@ -3233,7 +3233,6 @@ static MarshalInfo::MarshalType DoMarshalReturnValue(MetaSig& msig, CorNativeLinkFlags nlFlags, UINT argidx, // this is used for reverse pinvoke hresult swapping StubState* pss, - BOOL isInstanceMethod, int argOffset, DWORD dwStubFlags, MethodDesc *pMD, @@ -3283,7 +3282,6 @@ static MarshalInfo::MarshalType DoMarshalReturnValue(MetaSig& msig, SF_IsBestFit(dwStubFlags), SF_IsThrowOnUnmappableChar(dwStubFlags), TRUE, - isInstanceMethod, pMD, TRUE DEBUG_ARG(pDebugName) @@ -3476,12 +3474,6 @@ static void CreateNDirectStubWorker(StubState* pss, // return buffer argument as the first argument so as to match the native calling convention correctly. BOOL fMarshalReturnValueFirst = FALSE; - BOOL fReverseWithReturnBufferArg = FALSE; - // Only consider ThisCall methods to be instance methods. - // Techinically COM methods are also instance methods, but we don't want to change the behavior of the built-in - // COM abi work because there are many users that rely on the current behavior (for example WPF). - bool isInstanceMethod = 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. @@ -3489,8 +3481,6 @@ static void CreateNDirectStubWorker(StubState* pss, // to make sure we match the native signature correctly (when marshalling parameters, we add them to the native stub signature). if (!SF_IsHRESULTSwapping(dwStubFlags)) { - // 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 @@ -3502,17 +3492,9 @@ static void CreateNDirectStubWorker(StubState* pss, #ifdef UNIX_X86_ABI // For functions with value type class, managed and unmanaged calling convention differ fMarshalReturnValueFirst = HasRetBuffArgUnmanagedFixup(&msig); -#elif defined(TARGET_ARM) - fMarshalReturnValueFirst = (isInstanceMethod && isReturnTypeValueType) && 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 = (!SF_IsReverseStub(dwStubFlags) && HasRetBuffArg(&msig)) || (isInstanceMethod && isReturnTypeValueType); + fMarshalReturnValueFirst = HasRetBuffArg(&msig); #endif // UNIX_X86_ABI -#elif defined(TARGET_AMD64) || defined (TARGET_ARM64) - fMarshalReturnValueFirst = isInstanceMethod && isReturnTypeValueType; -#endif // defined(TARGET_X86) || defined(TARGET_ARM) -#ifdef _WIN32 - fReverseWithReturnBufferArg = fMarshalReturnValueFirst && SF_IsReverseStub(dwStubFlags); #endif } @@ -3558,7 +3540,6 @@ static void CreateNDirectStubWorker(StubState* pss, SF_IsBestFit(dwStubFlags), SF_IsThrowOnUnmappableChar(dwStubFlags), TRUE, - isInstanceMethod ? TRUE : FALSE, pMD, TRUE DEBUG_ARG(pSigDesc->m_pDebugName) @@ -3570,33 +3551,6 @@ static void CreateNDirectStubWorker(StubState* pss, 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, @@ -3605,7 +3559,6 @@ static void CreateNDirectStubWorker(StubState* pss, nlFlags, 0, pss, - isInstanceMethod, argOffset, dwStubFlags, pMD, @@ -3694,7 +3647,6 @@ static void CreateNDirectStubWorker(StubState* pss, nlFlags, argidx, pss, - isInstanceMethod, argOffset, dwStubFlags, pMD, @@ -3841,7 +3793,6 @@ static void CreateStructStub(ILStubState* pss, SF_IsBestFit(dwStubFlags), SF_IsThrowOnUnmappableChar(dwStubFlags), TRUE, - FALSE, pMD, TRUE DEBUG_ARG(pSigDesc->m_pDebugName) diff --git a/src/coreclr/src/vm/fieldmarshaler.cpp b/src/coreclr/src/vm/fieldmarshaler.cpp index 5b9133c1f1db19..1de899138c19bb 100644 --- a/src/coreclr/src/vm/fieldmarshaler.cpp +++ b/src/coreclr/src/vm/fieldmarshaler.cpp @@ -71,7 +71,6 @@ VOID ParseNativeType(Module* pModule, FALSE, // We only need validation of the native signature and the MARSHAL_TYPE_* FALSE, // so we don't need to accurately get the BestFitCustomAttribute data for this construction. FALSE, /* fEmitsIL */ - FALSE, /* onInstanceMethod */ nullptr, FALSE /* fUseCustomMarshal */ #ifdef _DEBUG diff --git a/src/coreclr/src/vm/ilmarshalers.h b/src/coreclr/src/vm/ilmarshalers.h index 3aae504fe8c989..1f19e6b1a604f0 100644 --- a/src/coreclr/src/vm/ilmarshalers.h +++ b/src/coreclr/src/vm/ilmarshalers.h @@ -353,12 +353,6 @@ class ILMarshaler return (0 != (dwMarshalFlags & MARSHAL_FLAG_RETVAL)); } - static inline bool IsInMemberFunction(DWORD dwMarshalFlags) - { - LIMITED_METHOD_CONTRACT; - return (0 != (dwMarshalFlags & MARSHAL_FLAG_IN_MEMBER_FUNCTION)); - } - static inline bool IsFieldMarshal(DWORD dwMarshalFlags) { LIMITED_METHOD_CONTRACT; @@ -602,7 +596,6 @@ class ILMarshaler bool byrefNativeReturn = false; CorElementType typ = ELEMENT_TYPE_VOID; UINT32 nativeSize = 0; - bool nativeMethodIsMemberFunction = IsInMemberFunction(dwMarshalFlags); // we need to convert value type return types to primitives as // JIT does not inline P/Invoke calls that return structures @@ -623,32 +616,19 @@ class ILMarshaler // 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, 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 + // For X86 Windows we bash the return type from struct to U1, U2, U4 or U8 // and use byrefNativeReturn for all other structs. - if (nativeMethodIsMemberFunction) + +#ifdef TARGET_X86 + switch (nativeSize) { -#ifdef TARGET_ARM - byrefNativeReturn = !nativeType.InternalToken.GetMethodTable()->IsNativeHFA(); -#else - byrefNativeReturn = true; -#endif + 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; + default: byrefNativeReturn = true; break; } - else - { -#ifdef TARGET_X86 - switch (nativeSize) - { - 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; - default: byrefNativeReturn = true; break; - } #endif // TARGET_X86 - } #endif // defined(TARGET_WINDOWS) // for UNIX_X86_ABI, we always need a return buffer argument for any size of structs. @@ -657,7 +637,7 @@ class ILMarshaler #endif } - if (IsHresultSwap(dwMarshalFlags) || (byrefNativeReturn && (IsCLRToNative(m_dwMarshalFlags) || nativeMethodIsMemberFunction))) + if (IsHresultSwap(dwMarshalFlags) || (byrefNativeReturn && IsCLRToNative(m_dwMarshalFlags))) { LocalDesc extraParamType = nativeType; extraParamType.MakeByRef(); @@ -779,10 +759,6 @@ class ILMarshaler 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/coreclr/src/vm/mlinfo.cpp b/src/coreclr/src/vm/mlinfo.cpp index 227b719840d0b3..96b66025973145 100644 --- a/src/coreclr/src/vm/mlinfo.cpp +++ b/src/coreclr/src/vm/mlinfo.cpp @@ -1188,7 +1188,6 @@ MarshalInfo::MarshalInfo(Module* pModule, BOOL BestFit, BOOL ThrowOnUnmappableChar, BOOL fEmitsIL, - BOOL onInstanceMethod, MethodDesc* pMD, BOOL fLoadCustomMarshal #ifdef _DEBUG @@ -1231,7 +1230,6 @@ MarshalInfo::MarshalInfo(Module* pModule, CorElementType corElemType = ELEMENT_TYPE_END; m_pMT = NULL; m_pMD = pMD; - m_onInstanceMethod = onInstanceMethod; #ifdef FEATURE_COMINTEROP m_fDispItf = FALSE; @@ -1377,7 +1375,7 @@ MarshalInfo::MarshalInfo(Module* pModule, // 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. // We don't normalize on return values of member functions since struct return values need to be treated as structures. - if (isParam || !onInstanceMethod) + if (isParam) { VerifyAndAdjustNormalizedType(pModule, sig, pTypeContext, &mtype, &nativeType); } @@ -2370,7 +2368,6 @@ MarshalInfo::MarshalInfo(Module* pModule, // (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*) @@ -2776,7 +2773,7 @@ DWORD CalculateArgumentMarshalFlags(BOOL byref, BOOL in, BOOL out, BOOL fMngToNa return dwMarshalFlags; } -DWORD CalculateReturnMarshalFlags(BOOL hrSwap, BOOL fMngToNative, BOOL onInstanceMethod) +DWORD CalculateReturnMarshalFlags(BOOL hrSwap, BOOL fMngToNative) { LIMITED_METHOD_CONTRACT; DWORD dwMarshalFlags = MARSHAL_FLAG_RETVAL; @@ -2791,11 +2788,6 @@ DWORD CalculateReturnMarshalFlags(BOOL hrSwap, BOOL fMngToNative, BOOL onInstanc dwMarshalFlags |= MARSHAL_FLAG_CLR_TO_NATIVE; } - if (onInstanceMethod) - { - dwMarshalFlags |= MARSHAL_FLAG_IN_MEMBER_FUNCTION; - } - return dwMarshalFlags; } @@ -2939,7 +2931,7 @@ void MarshalInfo::GenerateReturnIL(NDirectStubLinker* psl, } NewHolder pMarshaler = CreateILMarshaler(m_type, psl); - DWORD dwMarshalFlags = CalculateReturnMarshalFlags(retval, fMngToNative, m_onInstanceMethod); + DWORD dwMarshalFlags = CalculateReturnMarshalFlags(retval, fMngToNative); if (!pMarshaler->SupportsReturnMarshal(dwMarshalFlags, &resID)) { diff --git a/src/coreclr/src/vm/mlinfo.h b/src/coreclr/src/vm/mlinfo.h index 813bcbccbae37a..e3a25f2d9e8d05 100644 --- a/src/coreclr/src/vm/mlinfo.h +++ b/src/coreclr/src/vm/mlinfo.h @@ -52,8 +52,7 @@ enum MarshalFlags MARSHAL_FLAG_HRESULT_SWAP = 0x010, MARSHAL_FLAG_RETVAL = 0x020, // unused = 0x040, - MARSHAL_FLAG_FIELD = 0x080, - MARSHAL_FLAG_IN_MEMBER_FUNCTION = 0x100 + MARSHAL_FLAG_FIELD = 0x080 }; #include @@ -322,7 +321,6 @@ class MarshalInfo BOOL BestFit, BOOL ThrowOnUnmappableChar, BOOL fEmitsIL, - BOOL onInstanceMethod, MethodDesc* pMD = NULL, BOOL fUseCustomMarshal = TRUE #ifdef _DEBUG @@ -520,7 +518,6 @@ class MarshalInfo VARTYPE m_arrayElementType; int m_iArrayRank; BOOL m_nolowerbounds; // if managed type is SZARRAY, don't allow lower bounds - BOOL m_onInstanceMethod; // for NT_ARRAY only UINT32 m_multiplier; // multipler for "sizeis" From 4f09a4f20a04bcdc06c2fc793598c6d19a55e482 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 8 Jul 2020 12:10:04 -0700 Subject: [PATCH 07/73] Add reverse P/Invoke tests for ThisCall. --- .../Miscellaneous/ThisCall/ThisCallNative.cpp | 15 ++++ .../Miscellaneous/ThisCall/ThisCallTest.cs | 79 +++++++++++++++++-- 2 files changed, 87 insertions(+), 7 deletions(-) diff --git a/src/coreclr/tests/src/Interop/PInvoke/Miscellaneous/ThisCall/ThisCallNative.cpp b/src/coreclr/tests/src/Interop/PInvoke/Miscellaneous/ThisCall/ThisCallNative.cpp index 3424dbf20aa1f5..19dd3ec817beb1 100644 --- a/src/coreclr/tests/src/Interop/PInvoke/Miscellaneous/ThisCall/ThisCallNative.cpp +++ b/src/coreclr/tests/src/Interop/PInvoke/Miscellaneous/ThisCall/ThisCallNative.cpp @@ -55,3 +55,18 @@ extern "C" DLL_EXPORT C* STDMETHODCALLTYPE CreateInstanceOfC(float width, float { return new C(width, height); } + +extern "C" DLL_EXPORT SizeF STDMETHODCALLTYPE GetSizeFromManaged(C* c) +{ + return c->GetSize(); +} + +extern "C" DLL_EXPORT Width STDMETHODCALLTYPE GetWidthFromManaged(C* c) +{ + return c->GetWidth(); +} + +extern "C" DLL_EXPORT IntWrapper STDMETHODCALLTYPE GetHeightAsIntFromManaged(C* c) +{ + return c->GetHeightAsInt(); +} diff --git a/src/coreclr/tests/src/Interop/PInvoke/Miscellaneous/ThisCall/ThisCallTest.cs b/src/coreclr/tests/src/Interop/PInvoke/Miscellaneous/ThisCall/ThisCallTest.cs index a39a0fa128dac5..9af7fa703a812d 100644 --- a/src/coreclr/tests/src/Interop/PInvoke/Miscellaneous/ThisCall/ThisCallTest.cs +++ b/src/coreclr/tests/src/Interop/PInvoke/Miscellaneous/ThisCall/ThisCallTest.cs @@ -21,8 +21,8 @@ public struct VtableLayout public VtableLayout* vtable; private int c; - public readonly float width; - public readonly float height; + public float width; + public float height; } public struct SizeF @@ -50,11 +50,18 @@ public struct IntWrapper [DllImport(nameof(ThisCallNative))] public static extern C* CreateInstanceOfC(float width, float height); + + [DllImport(nameof(ThisCallNative))] + public static extern SizeF GetSizeFromManaged(C* c); + [DllImport(nameof(ThisCallNative))] + public static extern Width GetWidthFromManaged(C* c); + [DllImport(nameof(ThisCallNative))] + public static extern IntWrapper GetHeightAsIntFromManaged(C* c); } -class ThisCallTest +unsafe class ThisCallTest { - public unsafe static int Main(string[] args) + public static int Main(string[] args) { try { @@ -64,6 +71,9 @@ public unsafe static int Main(string[] args) Test8ByteHFA(instance); Test4ByteHFA(instance); Test4ByteNonHFA(instance); + Test8ByteHFAReverse(); + Test4ByteHFAReverse(); + Test4ByteNonHFAReverse(); } catch (System.Exception ex) { @@ -73,7 +83,7 @@ public unsafe static int Main(string[] args) return 100; } - private static unsafe void Test8ByteHFA(ThisCallNative.C* instance) + private static void Test8ByteHFA(ThisCallNative.C* instance) { ThisCallNative.GetSizeFn callback = Marshal.GetDelegateForFunctionPointer(instance->vtable->getSize); @@ -83,7 +93,7 @@ private static unsafe void Test8ByteHFA(ThisCallNative.C* instance) Assert.AreEqual(instance->height, result.height); } - private static unsafe void Test4ByteHFA(ThisCallNative.C* instance) + private static void Test4ByteHFA(ThisCallNative.C* instance) { ThisCallNative.GetWidthFn callback = Marshal.GetDelegateForFunctionPointer(instance->vtable->getWidth); @@ -92,7 +102,7 @@ private static unsafe void Test4ByteHFA(ThisCallNative.C* instance) Assert.AreEqual(instance->width, result.width); } - private static unsafe void Test4ByteNonHFA(ThisCallNative.C* instance) + private static void Test4ByteNonHFA(ThisCallNative.C* instance) { ThisCallNative.GetHeightAsIntFn callback = Marshal.GetDelegateForFunctionPointer(instance->vtable->getHeightAsInt); @@ -100,4 +110,59 @@ private static unsafe void Test4ByteNonHFA(ThisCallNative.C* instance) Assert.AreEqual((int)instance->height, result.i); } + + private static void Test8ByteHFAReverse() + { + ThisCallNative.C c = CreateCWithManagedVTable(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 Test4ByteHFAReverse() + { + ThisCallNative.C c = CreateCWithManagedVTable(2.0f, 3.0f); + ThisCallNative.Width result = ThisCallNative.GetWidthFromManaged(&c); + + Assert.AreEqual(c.width, result.width); + } + + private static void Test4ByteNonHFAReverse() + { + ThisCallNative.C c = CreateCWithManagedVTable(2.0f, 3.0f); + ThisCallNative.IntWrapper result = ThisCallNative.GetHeightAsIntFromManaged(&c); + + Assert.AreEqual((int)c.height, result.i); + } + + private static ThisCallNative.C CreateCWithManagedVTable(float width, float height) + { + return new ThisCallNative.C + { + vtable = ManagedVtable, + width = width, + height = height + }; + } + + private static ThisCallNative.C.VtableLayout* managedVtable; + + private static ThisCallNative.C.VtableLayout* ManagedVtable + { + get + { + if (managedVtable == null) + { + managedVtable = (ThisCallNative.C.VtableLayout*)Marshal.AllocHGlobal(sizeof(ThisCallNative.C.VtableLayout)); + managedVtable->getSize = Marshal.GetFunctionPointerForDelegate( + (ThisCallNative.GetSizeFn)((ThisCallNative.C* c) => new ThisCallNative.SizeF { width = c->width, height = c->height} )); + managedVtable->getWidth = Marshal.GetFunctionPointerForDelegate( + (ThisCallNative.GetWidthFn)((ThisCallNative.C* c) => new ThisCallNative.Width { width = c->width} )); + managedVtable->getHeightAsInt = Marshal.GetFunctionPointerForDelegate( + (ThisCallNative.GetHeightAsIntFn)((ThisCallNative.C* c) => new ThisCallNative.IntWrapper { i = (int)c->height} )); + } + return managedVtable; + } + } } From e4aaf91afd4122bcb7449d4b7d950840835e18c0 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 8 Jul 2020 12:16:01 -0700 Subject: [PATCH 08/73] Fix x86 build. --- src/coreclr/src/vm/comtoclrcall.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/src/vm/comtoclrcall.cpp b/src/coreclr/src/vm/comtoclrcall.cpp index 9d6fbc688d6c76..4c2195eb61cdd0 100644 --- a/src/coreclr/src/vm/comtoclrcall.cpp +++ b/src/coreclr/src/vm/comtoclrcall.cpp @@ -1023,7 +1023,7 @@ void ComCallMethodDesc::InitNativeInfo() 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, TRUE, NULL, FALSE + FALSE, 0, fsig.NumFixedArgs(), BestFit, ThrowOnUnmappableChar, FALSE, NULL, FALSE #ifdef _DEBUG , szDebugName, szDebugClassName, 0 #endif @@ -1121,7 +1121,7 @@ void ComCallMethodDesc::InitNativeInfo() MarshalInfo info(msig.GetModule(), msig.GetArgProps(), msig.GetSigTypeContext(), params[iArg], MarshalInfo::MARSHAL_SCENARIO_COMINTEROP, (CorNativeLinkType)0, (CorNativeLinkFlags)0, - TRUE, iArg, numArgs, BestFit, ThrowOnUnmappableChar, FALSE, TRUE, pMD, FALSE + TRUE, iArg, numArgs, BestFit, ThrowOnUnmappableChar, FALSE, pMD, FALSE #ifdef _DEBUG , szDebugName, szDebugClassName, iArg #endif From 3d66e5386d3d0fc531bded9aaaee5aa673445101 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 8 Jul 2020 12:35:02 -0700 Subject: [PATCH 09/73] Add enum test. --- .../Miscellaneous/ThisCall/ThisCallNative.cpp | 17 ++++++++- .../Miscellaneous/ThisCall/ThisCallTest.cs | 35 ++++++++++++++++++- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/src/coreclr/tests/src/Interop/PInvoke/Miscellaneous/ThisCall/ThisCallNative.cpp b/src/coreclr/tests/src/Interop/PInvoke/Miscellaneous/ThisCall/ThisCallNative.cpp index 19dd3ec817beb1..afbc606b348110 100644 --- a/src/coreclr/tests/src/Interop/PInvoke/Miscellaneous/ThisCall/ThisCallNative.cpp +++ b/src/coreclr/tests/src/Interop/PInvoke/Miscellaneous/ThisCall/ThisCallNative.cpp @@ -22,9 +22,14 @@ struct IntWrapper int i; }; +enum E : unsigned int +{ + Value = 42 +}; + class C { - int dummy = 0xcccccccc; + E dummy = E::Value; float width; float height; @@ -48,6 +53,11 @@ class C { return {(int)height}; } + + virtual E GetE() + { + return dummy; + } }; @@ -70,3 +80,8 @@ extern "C" DLL_EXPORT IntWrapper STDMETHODCALLTYPE GetHeightAsIntFromManaged(C* { return c->GetHeightAsInt(); } + +extern "C" DLL_EXPORT E STDMETHODCALLTYPE GetEFromManaged(C* c) +{ + return c->GetE(); +} diff --git a/src/coreclr/tests/src/Interop/PInvoke/Miscellaneous/ThisCall/ThisCallTest.cs b/src/coreclr/tests/src/Interop/PInvoke/Miscellaneous/ThisCall/ThisCallTest.cs index 9af7fa703a812d..bebbdfd02d07ea 100644 --- a/src/coreclr/tests/src/Interop/PInvoke/Miscellaneous/ThisCall/ThisCallTest.cs +++ b/src/coreclr/tests/src/Interop/PInvoke/Miscellaneous/ThisCall/ThisCallTest.cs @@ -17,10 +17,11 @@ public struct VtableLayout public IntPtr getSize; public IntPtr getWidth; public IntPtr getHeightAsInt; + public IntPtr getE; } public VtableLayout* vtable; - private int c; + public E dummy; public float width; public float height; } @@ -41,6 +42,11 @@ public struct IntWrapper public int i; } + public enum E : uint + { + Value = 42 + } + [UnmanagedFunctionPointer(CallingConvention.ThisCall)] public delegate SizeF GetSizeFn(C* c); [UnmanagedFunctionPointer(CallingConvention.ThisCall)] @@ -48,6 +54,9 @@ public struct IntWrapper [UnmanagedFunctionPointer(CallingConvention.ThisCall)] public delegate IntWrapper GetHeightAsIntFn(C* c); + [UnmanagedFunctionPointer(CallingConvention.ThisCall)] + public delegate E GetEFn(C* c); + [DllImport(nameof(ThisCallNative))] public static extern C* CreateInstanceOfC(float width, float height); @@ -57,6 +66,8 @@ public struct IntWrapper public static extern Width GetWidthFromManaged(C* c); [DllImport(nameof(ThisCallNative))] public static extern IntWrapper GetHeightAsIntFromManaged(C* c); + [DllImport(nameof(ThisCallNative))] + public static extern E GetEFromManaged(C* c); } unsafe class ThisCallTest @@ -71,9 +82,11 @@ public static int Main(string[] args) Test8ByteHFA(instance); Test4ByteHFA(instance); Test4ByteNonHFA(instance); + TestEnum(instance); Test8ByteHFAReverse(); Test4ByteHFAReverse(); Test4ByteNonHFAReverse(); + TestEnumReverse(); } catch (System.Exception ex) { @@ -111,6 +124,15 @@ private static void Test4ByteNonHFA(ThisCallNative.C* instance) Assert.AreEqual((int)instance->height, result.i); } + private static void TestEnum(ThisCallNative.C* instance) + { + ThisCallNative.GetEFn callback = Marshal.GetDelegateForFunctionPointer(instance->vtable->getE); + + ThisCallNative.E result = callback(instance); + + Assert.AreEqual(instance->dummy, result); + } + private static void Test8ByteHFAReverse() { ThisCallNative.C c = CreateCWithManagedVTable(2.0f, 3.0f); @@ -136,11 +158,20 @@ private static void Test4ByteNonHFAReverse() Assert.AreEqual((int)c.height, result.i); } + private static void TestEnumReverse() + { + ThisCallNative.C c = CreateCWithManagedVTable(2.0f, 3.0f); + ThisCallNative.E result = ThisCallNative.GetEFromManaged(&c); + + Assert.AreEqual(c.dummy, result); + } + private static ThisCallNative.C CreateCWithManagedVTable(float width, float height) { return new ThisCallNative.C { vtable = ManagedVtable, + dummy = ThisCallNative.E.Value, width = width, height = height }; @@ -161,6 +192,8 @@ private static ThisCallNative.C.VtableLayout* ManagedVtable (ThisCallNative.GetWidthFn)((ThisCallNative.C* c) => new ThisCallNative.Width { width = c->width} )); managedVtable->getHeightAsInt = Marshal.GetFunctionPointerForDelegate( (ThisCallNative.GetHeightAsIntFn)((ThisCallNative.C* c) => new ThisCallNative.IntWrapper { i = (int)c->height} )); + managedVtable->getE = Marshal.GetFunctionPointerForDelegate( + (ThisCallNative.GetEFn)((ThisCallNative.C* c) => c->dummy )); } return managedVtable; } From 4eee4589bcf931bbcb8b1f2cab9d37ce1ef7917d Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 8 Jul 2020 16:33:16 -0700 Subject: [PATCH 10/73] Remove interop-specific handling of x86 thiscall and get non trivial-primitive-wrapper struct returning thiscall working. --- src/coreclr/src/jit/compiler.cpp | 4 ++++ src/coreclr/src/jit/morph.cpp | 13 +++++++++---- src/coreclr/src/vm/ilmarshalers.h | 24 ------------------------ 3 files changed, 13 insertions(+), 28 deletions(-) diff --git a/src/coreclr/src/jit/compiler.cpp b/src/coreclr/src/jit/compiler.cpp index 867505ff520294..c252c41210858b 100644 --- a/src/coreclr/src/jit/compiler.cpp +++ b/src/coreclr/src/jit/compiler.cpp @@ -955,6 +955,10 @@ var_types Compiler::getReturnTypeForStruct(CORINFO_CLASS_HANDLE clsHnd, #endif // UNIX_AMD64_ABI +#ifdef UNIX_X86_ABI + canReturnInRegister = false; +#endif + // Check for cases where a small struct is returned in a register // via a primitive type. // diff --git a/src/coreclr/src/jit/morph.cpp b/src/coreclr/src/jit/morph.cpp index e5229176943764..b126ac5b873c8c 100644 --- a/src/coreclr/src/jit/morph.cpp +++ b/src/coreclr/src/jit/morph.cpp @@ -2768,15 +2768,20 @@ void Compiler::fgInitArgInfo(GenTreeCall* call) call->gtCallArgs->GetNode()->gtOper == GT_NOP); // the arg was already morphed to a register (fgMorph called twice) maxRegArgs = 1; +#ifdef UNIX_X86_ABI + // Add in the ret buff arg + if (callHasRetBuffArg) + maxRegArgs++; +#endif } else { maxRegArgs = 0; + + // Add in the ret buff arg + if (callHasRetBuffArg) + maxRegArgs++; } - - // Add in the ret buff arg - if (callHasRetBuffArg) - maxRegArgs++; } #endif // TARGET_X86 diff --git a/src/coreclr/src/vm/ilmarshalers.h b/src/coreclr/src/vm/ilmarshalers.h index 1f19e6b1a604f0..367bf957bd4b62 100644 --- a/src/coreclr/src/vm/ilmarshalers.h +++ b/src/coreclr/src/vm/ilmarshalers.h @@ -611,30 +611,6 @@ class ILMarshaler // the unmanaged type size is fixed nativeSize = wNativeSize; } - -#if defined(TARGET_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 X86 Windows we bash the return type from struct to U1, U2, U4 or U8 - // and use byrefNativeReturn for all other structs. - -#ifdef TARGET_X86 - switch (nativeSize) - { - 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; - default: byrefNativeReturn = true; break; - } -#endif // TARGET_X86 -#endif // defined(TARGET_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))) From a7e0b525484cc36984aab4c7868e6aa7ad0758c5 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 9 Jul 2020 09:51:58 -0700 Subject: [PATCH 11/73] Remove bashing the return type to int. --- src/coreclr/src/vm/mlinfo.cpp | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/coreclr/src/vm/mlinfo.cpp b/src/coreclr/src/vm/mlinfo.cpp index 96b66025973145..ce322ed0c57dd5 100644 --- a/src/coreclr/src/vm/mlinfo.cpp +++ b/src/coreclr/src/vm/mlinfo.cpp @@ -2363,22 +2363,6 @@ MarshalInfo::MarshalInfo(Module* pModule, m_type = MARSHAL_TYPE_BLITTABLEVALUECLASSWITHCOPYCTOR; } else -#ifdef TARGET_X86 - // JIT64 is not aware of normalized value types and this optimization - // (returning small value types by value in registers) is already done in JIT64. - if ( !m_byref // Permit register-sized structs as return values - && !isParam - && CorIsPrimitiveType(m_pMT->GetInternalCorElementType()) - && !IsUnmanagedValueTypeReturnedByRef(nativeSize) - && managedSize <= sizeof(void*) - && nativeSize <= sizeof(void*) - && !IsFieldScenario()) - { - m_type = MARSHAL_TYPE_GENERIC_4; - m_args.m_pMT = m_pMT; - } - else -#endif // TARGET_X86 { m_args.m_pMT = m_pMT; m_type = MARSHAL_TYPE_BLITTABLEVALUECLASS; From 8eb6846388f469122ec3e9edd8c9af7daa2784bd Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 10 Jul 2020 14:04:56 -0700 Subject: [PATCH 12/73] Don't unwrap single primitive field structs on x86 any more. RyuJIT seems to optimize them correctly now. Also this enables the JIT to correctly handle native instance method calls that return single primitive field structs. --- src/coreclr/src/vm/class.cpp | 66 ----------------------- src/coreclr/src/vm/class.h | 2 - src/coreclr/src/vm/methodtablebuilder.cpp | 32 ----------- 3 files changed, 100 deletions(-) diff --git a/src/coreclr/src/vm/class.cpp b/src/coreclr/src/vm/class.cpp index c0fee85546bf8a..928fb013e2fc8a 100644 --- a/src/coreclr/src/vm/class.cpp +++ b/src/coreclr/src/vm/class.cpp @@ -1291,72 +1291,6 @@ void ClassLoader::ValidateMethodsWithCovariantReturnTypes(MethodTable* pMT) } } -//******************************************************************************* -// This is the routine that computes the internal type of a given type. It normalizes -// structs that have only one field (of int/ptr sized values), to be that underlying type. -// -// * see code:MethodTable#KindsOfElementTypes for more -// * It get used by code:TypeHandle::GetInternalCorElementType -CorElementType EEClass::ComputeInternalCorElementTypeForValueType(MethodTable * pMT) -{ - CONTRACTL { - THROWS; - GC_TRIGGERS; - } CONTRACTL_END; - - if (pMT->GetNumInstanceFields() == 1 && (!pMT->HasLayout() - || pMT->GetNumInstanceFieldBytes() == 4 -#ifdef TARGET_64BIT - || pMT->GetNumInstanceFieldBytes() == 8 -#endif // TARGET_64BIT - )) // Don't do the optimization if we're getting specified anything but the trivial layout. - { - FieldDesc * pFD = pMT->GetApproxFieldDescListRaw(); - CorElementType type = pFD->GetFieldType(); - - if (type == ELEMENT_TYPE_VALUETYPE) - { - //@todo: Is it more apropos to call LookupApproxFieldTypeHandle() here? - TypeHandle fldHnd = pFD->GetApproxFieldTypeHandleThrowing(); - CONSISTENCY_CHECK(!fldHnd.IsNull()); - - type = fldHnd.GetInternalCorElementType(); - } - - switch (type) - { - // "DDB 20951: vc8 unmanaged pointer bug." - // If ELEMENT_TYPE_PTR were returned, Compiler::verMakeTypeInfo would have problem - // creating a TI_STRUCT out of CORINFO_TYPE_PTR. - // As a result, the importer would not be able to realize that the thing on the stack - // is an instance of a valuetype (that contains one single "void*" field), rather than - // a pointer to a valuetype. - // Returning ELEMENT_TYPE_U allows verMakeTypeInfo to go down the normal code path - // for creating a TI_STRUCT. - case ELEMENT_TYPE_PTR: - type = ELEMENT_TYPE_U; - - case ELEMENT_TYPE_I: - case ELEMENT_TYPE_U: - case ELEMENT_TYPE_I4: - case ELEMENT_TYPE_U4: -#ifdef TARGET_64BIT - case ELEMENT_TYPE_I8: - case ELEMENT_TYPE_U8: -#endif // TARGET_64BIT - - { - return type; - } - - default: - break; - } - } - - return ELEMENT_TYPE_VALUETYPE; -} - //******************************************************************************* // // Debugger notification diff --git a/src/coreclr/src/vm/class.h b/src/coreclr/src/vm/class.h index 441f5bfe033291..85486abdaae951 100644 --- a/src/coreclr/src/vm/class.h +++ b/src/coreclr/src/vm/class.h @@ -795,8 +795,6 @@ class EEClass // DO NOT CREATE A NEW EEClass USING NEW! void EnumMemoryRegions(CLRDataEnumMemoryFlags flags, MethodTable *pMT); #endif - static CorElementType ComputeInternalCorElementTypeForValueType(MethodTable * pMT); - /************************************ * INSTANCE MEMBER VARIABLES ************************************/ diff --git a/src/coreclr/src/vm/methodtablebuilder.cpp b/src/coreclr/src/vm/methodtablebuilder.cpp index c1284a68db35e5..56d2027ca804ea 100644 --- a/src/coreclr/src/vm/methodtablebuilder.cpp +++ b/src/coreclr/src/vm/methodtablebuilder.cpp @@ -9623,19 +9623,6 @@ void MethodTableBuilder::CheckForSystemTypes() _ASSERTE(g_pByReferenceClass != NULL); _ASSERTE(g_pByReferenceClass->IsByRefLike()); -#ifdef TARGET_X86 - if (GetCl() == g_pByReferenceClass->GetCl()) - { - // x86 by default treats the type of ByReference as the actual type of its IntPtr field, see calls to - // ComputeInternalCorElementTypeForValueType in this file. This is a special case where the struct needs to be - // treated as a value type so that its field can be considered as a byref pointer. - _ASSERTE(pMT->GetFlag(MethodTable::enum_flag_Category_Mask) == MethodTable::enum_flag_Category_PrimitiveValueType); - pMT->ClearFlag(MethodTable::enum_flag_Category_Mask); - pMT->SetInternalCorElementType(ELEMENT_TYPE_VALUETYPE); - return; - } -#endif - _ASSERTE(g_pNullableClass->IsNullable()); // Pre-compute whether the class is a Nullable so that code:Nullable::IsNullableType is efficient @@ -9703,17 +9690,6 @@ void MethodTableBuilder::CheckForSystemTypes() { pMT->SetIsNullable(); } -#ifdef TARGET_X86 - else if (strcmp(name, g_ByReferenceName) == 0) - { - // x86 by default treats the type of ByReference as the actual type of its IntPtr field, see calls to - // ComputeInternalCorElementTypeForValueType in this file. This is a special case where the struct needs to be - // treated as a value type so that its field can be considered as a byref pointer. - _ASSERTE(pMT->GetFlag(MethodTable::enum_flag_Category_Mask) == MethodTable::enum_flag_Category_PrimitiveValueType); - pMT->ClearFlag(MethodTable::enum_flag_Category_Mask); - pMT->SetInternalCorElementType(ELEMENT_TYPE_VALUETYPE); - } -#endif #ifndef TARGET_X86 else if (strcmp(name, g_RuntimeArgumentHandleName) == 0) { @@ -10378,15 +10354,7 @@ MethodTableBuilder::SetupMethodTable2( } else { -#ifdef TARGET_X86 - // JIT64 is not aware of normalized value types and this - // optimization (return small value types by value in registers) - // is already done in JIT64. - OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(CLASS_LOADED); - normalizedType = EEClass::ComputeInternalCorElementTypeForValueType(pMT); -#else normalizedType = ELEMENT_TYPE_VALUETYPE; -#endif } } pMT->SetInternalCorElementType(normalizedType); From 110c2aee26f78a85319cfce9f9391f32e40250c8 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 10 Jul 2020 14:08:26 -0700 Subject: [PATCH 13/73] Pass the retbuf arg on the stack as the first argument (inserted last since the arguments have already been reversed) on x86. --- src/coreclr/src/jit/importer.cpp | 29 +++++++++++++++++++++++++++-- src/coreclr/src/jit/morph.cpp | 11 ++++------- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/src/coreclr/src/jit/importer.cpp b/src/coreclr/src/jit/importer.cpp index e5c8ae4fecfb50..40038561f5e15a 100644 --- a/src/coreclr/src/jit/importer.cpp +++ b/src/coreclr/src/jit/importer.cpp @@ -1191,9 +1191,34 @@ GenTree* Compiler::impAssignStructPtr(GenTree* destAddr, #if defined(TARGET_WINDOWS) && !defined(TARGET_ARM) // Unmanaged instance methods on Windows need the retbuf arg after the first (this) parameter - if ((srcCall->gtFlags & GTF_CALL_UNMANAGED) && (srcCall->gtCallMoreFlags & GTF_CALL_M_UNMGD_INST_CALL)) + if (srcCall->IsUnmanaged()) { - GenTreeCall::Use* thisArg = gtInsertNewCallArgAfter(destAddr, srcCall->gtCallArgs); + if (srcCall->gtCallMoreFlags & GTF_CALL_M_UNMGD_INST_CALL) + { + GenTreeCall::Use* thisArg = gtInsertNewCallArgAfter(destAddr, srcCall->gtCallArgs); + } + else + { +#ifdef TARGET_X86 + // 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) + { + srcCall->gtCallArgs = gtPrependNewCallArg(destAddr, srcCall->gtCallArgs); + } + else + { + for(; lastArg->GetNext() != nullptr; lastArg = lastArg->GetNext()); + lastArg->SetNext(gtPrependNewCallArg(destAddr, lastArg->GetNext())); + } +#else + // insert the return value buffer into the argument list as first byref parameter + srcCall->gtCallArgs = gtPrependNewCallArg(destAddr, srcCall->gtCallArgs); +#endif + } } else #endif diff --git a/src/coreclr/src/jit/morph.cpp b/src/coreclr/src/jit/morph.cpp index b126ac5b873c8c..d21b463e0c5713 100644 --- a/src/coreclr/src/jit/morph.cpp +++ b/src/coreclr/src/jit/morph.cpp @@ -2768,20 +2768,17 @@ void Compiler::fgInitArgInfo(GenTreeCall* call) call->gtCallArgs->GetNode()->gtOper == GT_NOP); // the arg was already morphed to a register (fgMorph called twice) maxRegArgs = 1; -#ifdef UNIX_X86_ABI - // Add in the ret buff arg - if (callHasRetBuffArg) - maxRegArgs++; -#endif + } else { maxRegArgs = 0; - + } +#ifdef UNIX_X86_ABI // Add in the ret buff arg if (callHasRetBuffArg) maxRegArgs++; - } +#endif } #endif // TARGET_X86 From 96336f57a1a76abad735963ee8c3d44fdf6543ec Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 13 Jul 2020 09:29:22 -0700 Subject: [PATCH 14/73] Remove extra newline --- src/coreclr/src/jit/morph.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/coreclr/src/jit/morph.cpp b/src/coreclr/src/jit/morph.cpp index d21b463e0c5713..be7d2d62d36660 100644 --- a/src/coreclr/src/jit/morph.cpp +++ b/src/coreclr/src/jit/morph.cpp @@ -2768,7 +2768,6 @@ void Compiler::fgInitArgInfo(GenTreeCall* call) call->gtCallArgs->GetNode()->gtOper == GT_NOP); // the arg was already morphed to a register (fgMorph called twice) maxRegArgs = 1; - } else { From 48e3fce65e3f0916110cef476a70620baccbc8b6 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 13 Jul 2020 13:54:49 -0700 Subject: [PATCH 15/73] Enable returning 8-byte structs in multiple registers on x86 and add a test in interop to validate. --- src/coreclr/src/jit/compiler.cpp | 18 ++++++- src/coreclr/src/jit/compiler.h | 8 +-- src/coreclr/src/jit/gentree.cpp | 12 +++++ src/coreclr/src/jit/importer.cpp | 51 +++++++++++++++++++ .../PInvoke/MarshalStructAsLayoutExp.cs | 9 ++++ .../PInvoke/MarshalStructAsParamDLL.cpp | 5 ++ 6 files changed, 98 insertions(+), 5 deletions(-) diff --git a/src/coreclr/src/jit/compiler.cpp b/src/coreclr/src/jit/compiler.cpp index c252c41210858b..67247d67994b2f 100644 --- a/src/coreclr/src/jit/compiler.cpp +++ b/src/coreclr/src/jit/compiler.cpp @@ -1076,8 +1076,24 @@ var_types Compiler::getReturnTypeForStruct(CORINFO_CLASS_HANDLE clsHnd, howToReturnStruct = SPK_ByReference; useType = TYP_UNKNOWN; } +#elif defined(TARGET_X86) -#elif defined(TARGET_ARM) || defined(TARGET_X86) + // Only 8-byte structs are return in multiple registers + if (structSize == MAX_RET_MULTIREG_BYTES) + { + // setup wbPassType and useType indicate that this is return by value in multiple registers + howToReturnStruct = SPK_ByValue; + useType = TYP_STRUCT; + } + else + { + // Otherwise we return this struct using a return buffer + // setup wbPassType and useType indicate that this is returned using a return buffer register + // (reference to a return buffer) + howToReturnStruct = SPK_ByReference; + useType = TYP_UNKNOWN; + } +#elif defined(TARGET_ARM) // Otherwise we return this struct using a return buffer // setup wbPassType and useType indicate that this is returned using a return buffer register diff --git a/src/coreclr/src/jit/compiler.h b/src/coreclr/src/jit/compiler.h index e512fd78079547..04d0342888c857 100644 --- a/src/coreclr/src/jit/compiler.h +++ b/src/coreclr/src/jit/compiler.h @@ -9160,8 +9160,8 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX { #if FEATURE_MULTIREG_RET #if defined(TARGET_X86) - // On x86 only 64-bit longs are returned in multiple registers - return varTypeIsLong(info.compRetNativeType); + // On x86, 64-bit longs are returned in multiple registers + return varTypeIsLong(info.compRetNativeType) || (varTypeIsStruct(info.compRetNativeType) && (info.compRetBuffArg == BAD_VAR_NUM)); #else // targets: X64-UNIX, ARM64 or ARM32 // On all other targets that support multireg return values: // Methods returning a struct in multiple registers have a return value of TYP_STRUCT. @@ -9185,8 +9185,8 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX { #if FEATURE_MULTIREG_RET #if defined(TARGET_X86) - // On x86 only 64-bit longs are returned in multiple registers - return varTypeIsLong(info.compRetNativeType); + // On x86, 64-bit longs are returned in multiple registers + return varTypeIsLong(info.compRetNativeType) || (varTypeIsStruct(info.compRetNativeType) && (info.compRetBuffArg == BAD_VAR_NUM)); #else // targets: X64-UNIX, ARM64 or ARM32 #if defined(TARGET_ARM64) // TYP_SIMD16 is returned in one register. diff --git a/src/coreclr/src/jit/gentree.cpp b/src/coreclr/src/jit/gentree.cpp index 6a316b1dcf2f12..8c6fb7273b05d0 100644 --- a/src/coreclr/src/jit/gentree.cpp +++ b/src/coreclr/src/jit/gentree.cpp @@ -18877,6 +18877,18 @@ void ReturnTypeDesc::InitializeStructReturnType(Compiler* comp, CORINFO_CLASS_HA m_regType[i] = comp->getJitGCType(gcPtrs[i]); } +#elif defined(TARGET_X86) + + // an 8-byte struct returned using two registers + assert(structSize == 8); + + BYTE gcPtrs[2] = {TYPE_GC_NONE, TYPE_GC_NONE}; + comp->info.compCompHnd->getClassGClayout(retClsHnd, &gcPtrs[0]); + for (unsigned i = 0; i < 2; ++i) + { + m_regType[i] = comp->getJitGCType(gcPtrs[i]); + } + #else // TARGET_XXX // This target needs support here! diff --git a/src/coreclr/src/jit/importer.cpp b/src/coreclr/src/jit/importer.cpp index 40038561f5e15a..c862a298a4621b 100644 --- a/src/coreclr/src/jit/importer.cpp +++ b/src/coreclr/src/jit/importer.cpp @@ -9174,6 +9174,33 @@ GenTree* Compiler::impFixupStructReturnType(GenTree* op, CORINFO_CLASS_HANDLE re // No VarArgs for CoreCLR on x64 Unix assert(!info.compIsVarArgs); + // Is method returning a multi-reg struct? + if (varTypeIsStruct(info.compRetNativeType) && IsMultiRegReturnedType(retClsHnd)) + { + // In case of multi-reg struct return, we force IR to be one of the following: + // GT_RETURN(lclvar) or GT_RETURN(call). If op is anything other than a + // lclvar or call, it is assigned to a temp to create: temp = op and GT_RETURN(tmp). + + if (op->gtOper == GT_LCL_VAR) + { + // Make sure that this struct stays in memory and doesn't get promoted. + unsigned lclNum = op->AsLclVarCommon()->GetLclNum(); + lvaTable[lclNum].lvIsMultiRegRet = true; + + // TODO-1stClassStructs: Handle constant propagation and CSE-ing of multireg returns. + op->gtFlags |= GTF_DONT_CSE; + + return op; + } + + if (op->gtOper == GT_CALL) + { + return op; + } + + return impAssignMultiRegTypeToVar(op, retClsHnd); + } +#elif defined(TARGET_X86) // Is method returning a multi-reg struct? if (varTypeIsStruct(info.compRetNativeType) && IsMultiRegReturnedType(retClsHnd)) { @@ -16849,6 +16876,30 @@ bool Compiler::impReturnInstruction(int prefixFlags, OPCODE& opcode) } } else +#elif defined(TARGET_X86) + ReturnTypeDesc retTypeDesc; + retTypeDesc.InitializeStructReturnType(this, retClsHnd); + unsigned retRegCount = retTypeDesc.GetReturnRegCount(); + + if (retRegCount != 0) + { + assert(!iciCall->HasRetBufArg()); + assert(retRegCount == MAX_RET_REG_COUNT); + if (fgNeedReturnSpillTemp()) + { + if (!impInlineInfo->retExpr) + { + // The inlinee compiler has figured out the type of the temp already. Use it here. + impInlineInfo->retExpr = + gtNewLclvNode(lvaInlineeReturnSpillTemp, lvaTable[lvaInlineeReturnSpillTemp].lvType); + } + } + else + { + impInlineInfo->retExpr = op2; + } + } + else #endif // defined(TARGET_ARM64) { assert(iciCall->HasRetBufArg()); diff --git a/src/coreclr/tests/src/Interop/StructMarshalling/PInvoke/MarshalStructAsLayoutExp.cs b/src/coreclr/tests/src/Interop/StructMarshalling/PInvoke/MarshalStructAsLayoutExp.cs index 9cffdfaccc4757..fb2d2cf5cde7a4 100644 --- a/src/coreclr/tests/src/Interop/StructMarshalling/PInvoke/MarshalStructAsLayoutExp.cs +++ b/src/coreclr/tests/src/Interop/StructMarshalling/PInvoke/MarshalStructAsLayoutExp.cs @@ -211,6 +211,8 @@ public static int Main() #endregion [DllImport("MarshalStructAsParam")] static extern LongStructPack16Explicit GetLongStruct(long l1, long l2); + [DllImport("MarshalStructAsParam")] + static extern IntStructPack8Explicit GetIntStruct(int i, int j); [DllImport("MarshalStructAsParam")] static extern bool MarshalStructAsParam_AsExpByValOverlappingLongFloat(OverlappingLongFloat str, long expected); @@ -1680,5 +1682,12 @@ private static void RunMarshalStructAsReturn() Console.WriteLine("Failed to return LongStructPack16Explicit."); failures++; } + + IntStructPack8Explicit intStruct = GetIntStruct(12345, 678910); + if(intStruct.i1 != 12345 || intStruct.i2 != 678910) + { + Console.WriteLine("Failed to return IntStructPack8Explicit."); + failures++; + } } } diff --git a/src/coreclr/tests/src/Interop/StructMarshalling/PInvoke/MarshalStructAsParamDLL.cpp b/src/coreclr/tests/src/Interop/StructMarshalling/PInvoke/MarshalStructAsParamDLL.cpp index 74ab359cd67b59..a7f5d17de6c488 100644 --- a/src/coreclr/tests/src/Interop/StructMarshalling/PInvoke/MarshalStructAsParamDLL.cpp +++ b/src/coreclr/tests/src/Interop/StructMarshalling/PInvoke/MarshalStructAsParamDLL.cpp @@ -1263,6 +1263,11 @@ extern "C" DLL_EXPORT MultipleBools STDMETHODCALLTYPE GetBools(BOOL b1, BOOL b2) return {b1, b2}; } +extern "C" DLL_EXPORT IntStructPack8Explicit STDMETHODCALLTYPE GetIntStruct(int i, int j) +{ + return {i, j}; +} + using IntIntDelegate = int (STDMETHODCALLTYPE*)(int a); struct DelegateFieldMarshaling From b83c60a2cce47b78e7538f10a62bad813e12c93e Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 13 Jul 2020 17:20:53 -0700 Subject: [PATCH 16/73] Since the x86 stub linker path emulates the stdcall calling convention for thiscall Reverse P/Invoke stubs, update the stub linker to handle the case where a return value would be an enregistered return on stdcall but is returned via a return buffer for thiscall. --- src/coreclr/src/vm/callingconvention.h | 35 ++++++++++++++++++-- src/coreclr/src/vm/dllimportcallback.cpp | 42 +++++++++++++++++++----- src/coreclr/src/vm/dllimportcallback.h | 12 ++++--- 3 files changed, 73 insertions(+), 16 deletions(-) diff --git a/src/coreclr/src/vm/callingconvention.h b/src/coreclr/src/vm/callingconvention.h index f57232ccb97db8..1c0d02a70e5173 100644 --- a/src/coreclr/src/vm/callingconvention.h +++ b/src/coreclr/src/vm/callingconvention.h @@ -1487,7 +1487,7 @@ void ArgIteratorTemplate::ComputeReturnFlags() break; case ELEMENT_TYPE_VALUETYPE: -#ifdef ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE +#ifdef ENREGISTERED_RETURNTYPE_MAXSIZE { _ASSERTE(!thValueType.IsNull()); @@ -1547,11 +1547,11 @@ void ArgIteratorTemplate::ComputeReturnFlags() } #endif - if (size <= ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE) + if (size <= ENREGISTERED_RETURNTYPE_MAXSIZE) break; #endif // UNIX_AMD64_ABI } -#endif // ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE +#endif // ENREGISTERED_RETURNTYPE_MAXSIZE // Value types are returned using return buffer by default flags |= RETURN_HAS_RET_BUFFER; @@ -1828,6 +1828,35 @@ class ArgIterator : public ArgIteratorTemplate return FALSE; #endif } + + BOOL HasValueTypeReturn() + { + WRAPPER_NO_CONTRACT; + + TypeHandle thValueType; + CorElementType type = m_pSig->GetReturnTypeNormalized(&thValueType); + return type == ELEMENT_TYPE_VALUETYPE && !thValueType.IsEnum(); + } + +#if defined(TARGET_X86) && !defined(FEATURE_STUBS_AS_IL) + BOOL HasMultiRegReturnStruct() + { + WRAPPER_NO_CONTRACT; + + TypeHandle thValueType; + CorElementType type = m_pSig->GetReturnTypeNormalized(&thValueType); + if (type != ELEMENT_TYPE_VALUETYPE || thValueType.IsEnum()) + { + return FALSE; + } + unsigned int structSize = thValueType.GetSize(); + if (structSize == ENREGISTERED_RETURNTYPE_MAXSIZE) + { + return TRUE; + } + return FALSE; + } +#endif }; // Conventience helper diff --git a/src/coreclr/src/vm/dllimportcallback.cpp b/src/coreclr/src/vm/dllimportcallback.cpp index 633d07fa803f25..8e899b8c0479b9 100644 --- a/src/coreclr/src/vm/dllimportcallback.cpp +++ b/src/coreclr/src/vm/dllimportcallback.cpp @@ -473,6 +473,19 @@ VOID UMEntryThunk::CompileUMThunkWorker(UMThunkStubInfo *pInfo, } else { + if (pInfo->m_wFlags & umtmlEnregRetValToBuf) + { + pcpusl->X86EmitPushReg(kEDI); // Save EDI register + // Move the return value from the enregisterd return from the JIT + // to the return buffer that the C++ compiler expects. + pcpusl->X86EmitIndexRegLoad(kEDI, kEBX, retbufofs); + pcpusl->X86EmitIndexRegStore(kEDI, 0x0, kEAX); + if (pInfo->m_wFlags & umtmlMultiregRetVal) + { + pcpusl->X86EmitIndexRegStore(kEDI, 0x4 /* skip EAX half of the return value */, kEDX); + } + pcpusl->X86EmitPopReg(kEDI); // Restore EDI register + } // pretend that the method returned the ret buf hidden argument // (the structure ptr); C++ compiler seems to rely on this @@ -766,6 +779,10 @@ Stub *UMThunkMarshInfo::CompileNExportThunk(LoaderHeap *pLoaderHeap, PInvokeStat UINT nOffset = 0; int numRegistersUsed = 0; int numStackSlotsIndex = nStackBytes / STACK_ELEM_SIZE; + + // This could have been set in the UnmanagedCallersOnly scenario. + if (m_callConv == UINT16_MAX) + m_callConv = static_cast(pSigInfo->GetCallConv()); // process this if (!fIsStatic) @@ -775,11 +792,16 @@ Stub *UMThunkMarshInfo::CompileNExportThunk(LoaderHeap *pLoaderHeap, PInvokeStat } // process the return buffer parameter - if (argit.HasRetBuffArg()) + if (argit.HasRetBuffArg() || (m_callConv == pmCallConvThiscall && argit.HasValueTypeReturn())) { - numRegistersUsed++; - _ASSERTE(numRegistersUsed - 1 < NUM_ARGUMENT_REGISTERS); - psrcofsregs[NUM_ARGUMENT_REGISTERS - numRegistersUsed] = nOffset; + // Only copy the retbuf arg from the src call when the managed call will also + // have a return buffer. + if (argit.HasRetBuffArg()) + { + numRegistersUsed++; + _ASSERTE(numRegistersUsed - 1 < NUM_ARGUMENT_REGISTERS); + psrcofsregs[NUM_ARGUMENT_REGISTERS - numRegistersUsed] = nOffset; + } retbufofs = nOffset; nOffset += StackElemSize(sizeof(LPVOID)); @@ -846,10 +868,6 @@ Stub *UMThunkMarshInfo::CompileNExportThunk(LoaderHeap *pLoaderHeap, PInvokeStat m_cbActualArgSize = cbActualArgSize; - // This could have been set in the UnmanagedCallersOnly scenario. - if (m_callConv == UINT16_MAX) - m_callConv = static_cast(pSigInfo->GetCallConv()); - UMThunkStubInfo stubInfo; memset(&stubInfo, 0, sizeof(stubInfo)); @@ -876,6 +894,14 @@ Stub *UMThunkMarshInfo::CompileNExportThunk(LoaderHeap *pLoaderHeap, PInvokeStat { stubInfo.m_wFlags |= umtmlThisCallHiddenArg; } + else if (argit.HasValueTypeReturn()) + { + stubInfo.m_wFlags |= umtmlThisCallHiddenArg | umtmlEnregRetValToBuf; + if (argit.HasMultiRegReturnStruct()) + { + stubInfo.m_wFlags |= umtmlMultiregRetVal; + } + } } } stubInfo.m_cbRetPop = m_cbRetPop; diff --git a/src/coreclr/src/vm/dllimportcallback.h b/src/coreclr/src/vm/dllimportcallback.h index b92b8e8784ccd4..1932ec07e5f239 100644 --- a/src/coreclr/src/vm/dllimportcallback.h +++ b/src/coreclr/src/vm/dllimportcallback.h @@ -19,13 +19,15 @@ enum UMThunkStubFlags { - umtmlIsStatic = 0x0001, - umtmlThisCall = 0x0002, - umtmlThisCallHiddenArg = 0x0004, - umtmlFpu = 0x0008, + umtmlIsStatic = 0x0001, + umtmlThisCall = 0x0002, + umtmlThisCallHiddenArg = 0x0004, + umtmlFpu = 0x0008, + umtmlEnregRetValToBuf = 0x0010, + umtmlMultiregRetVal = 0x0020, #ifdef TARGET_X86 // the signature is trivial so stub need not be generated and the target can be called directly - umtmlSkipStub = 0x0080, + umtmlSkipStub = 0x0080, #endif // TARGET_X86 }; From f3bdf580312df8958a8056101691ffafc2c6858a Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 14 Jul 2020 09:42:11 -0700 Subject: [PATCH 17/73] Remove unused code for manual return buffers from the interop subsystem. --- src/coreclr/src/vm/dllimport.cpp | 157 +++++++++++------------------- src/coreclr/src/vm/ilmarshalers.h | 92 ++--------------- 2 files changed, 63 insertions(+), 186 deletions(-) diff --git a/src/coreclr/src/vm/dllimport.cpp b/src/coreclr/src/vm/dllimport.cpp index 15b69f553afc55..658982ab1743f5 100644 --- a/src/coreclr/src/vm/dllimport.cpp +++ b/src/coreclr/src/vm/dllimport.cpp @@ -3468,36 +3468,6 @@ static void CreateNDirectStubWorker(StubState* pss, UINT nativeStackSize = (SF_IsCOMStub(dwStubFlags) ? sizeof(SLOT) : 0); bool fStubNeedsCOM = SF_IsCOMStub(dwStubFlags); - // 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 so as to match the native calling convention correctly. - BOOL fMarshalReturnValueFirst = 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)) - { -#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 - // argument. This matches the ABI i.e. return buffer is passed as first arg. So native target will get the - // return buffer in correct register. - // The return structure secret arg comes first, however byvalue return is processed at - // the end because it could be the HRESULT-swapped argument which always comes last. - -#ifdef UNIX_X86_ABI - // For functions with value type class, managed and unmanaged calling convention differ - fMarshalReturnValueFirst = HasRetBuffArgUnmanagedFixup(&msig); -#else - fMarshalReturnValueFirst = HasRetBuffArg(&msig); -#endif // UNIX_X86_ABI -#endif - } - // // Marshal the arguments // @@ -3551,48 +3521,6 @@ static void CreateNDirectStubWorker(StubState* pss, int argidx = 1; int nativeArgIndex = 0; - if (fMarshalReturnValueFirst) - { - marshalType = DoMarshalReturnValue(msig, - pParamTokenArray, - nlType, - nlFlags, - 0, - pss, - 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) { // @@ -3638,37 +3566,62 @@ static void CreateNDirectStubWorker(StubState* pss, argOffset++; } - if (!fMarshalReturnValueFirst) - { - // This could be a HRESULT-swapped argument so it must come last. - marshalType = DoMarshalReturnValue(msig, - pParamTokenArray, - nlType, - nlFlags, - argidx, - pss, - argOffset, - dwStubFlags, - pMD, - nativeStackSize, - fStubNeedsCOM, - nativeArgIndex - DEBUG_ARG(pSigDesc->m_pDebugName) - DEBUG_ARG(pSigDesc->m_pDebugClassName) - ); - - // If the return value is a SafeHandle or CriticalHandle, mark the stub method. - // Interop methods that use this stub will have an implicit reliability contract - // (see code:TAStackCrawlCallBack). - if (!SF_IsHRESULTSwapping(dwStubFlags)) - { - if (marshalType == MarshalInfo::MARSHAL_TYPE_SAFEHANDLE || - marshalType == MarshalInfo::MARSHAL_TYPE_CRITICALHANDLE) - { - if (pMD->IsDynamicMethod()) - pMD->AsDynamicMethodDesc()->SetUnbreakable(true); - } + marshalType = DoMarshalReturnValue(msig, + pParamTokenArray, + nlType, + nlFlags, + argidx, + pss, + argOffset, + dwStubFlags, + pMD, + nativeStackSize, + fStubNeedsCOM, + nativeArgIndex + DEBUG_ARG(pSigDesc->m_pDebugName) + DEBUG_ARG(pSigDesc->m_pDebugClassName) + ); + + // If the return value is a SafeHandle or CriticalHandle, mark the stub method. + // Interop methods that use this stub will have an implicit reliability contract + // (see code:TAStackCrawlCallBack). + if (!SF_IsHRESULTSwapping(dwStubFlags)) + { + if (marshalType == MarshalInfo::MARSHAL_TYPE_SAFEHANDLE || + marshalType == MarshalInfo::MARSHAL_TYPE_CRITICALHANDLE) + { + if (pMD->IsDynamicMethod()) + pMD->AsDynamicMethodDesc()->SetUnbreakable(true); + } + } + + 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); } +#if defined(TARGET_WINDOWS) && !defined(TARGET_ARM) + else if (fThisCall) + { + nativeStackSize += sizeof(LPVOID); + } +#endif } if (SF_IsHRESULTSwapping(dwStubFlags)) diff --git a/src/coreclr/src/vm/ilmarshalers.h b/src/coreclr/src/vm/ilmarshalers.h index 367bf957bd4b62..7674c22bbf8413 100644 --- a/src/coreclr/src/vm/ilmarshalers.h +++ b/src/coreclr/src/vm/ilmarshalers.h @@ -593,61 +593,20 @@ class ILMarshaler LocalDesc nativeType = GetNativeType(); LocalDesc managedType = GetManagedType(); - bool byrefNativeReturn = false; - CorElementType typ = ELEMENT_TYPE_VOID; - UINT32 nativeSize = 0; - - // we need to convert value type return types to primitives as - // JIT does not inline P/Invoke calls that return structures - if (nativeType.IsValueClass()) - { - if (wNativeSize == VARIABLESIZE) - { - // the unmanaged type size is variable - nativeSize = m_pargs->m_pMT->GetNativeSize(); - } - else - { - // the unmanaged type size is fixed - nativeSize = wNativeSize; - } - } - - if (IsHresultSwap(dwMarshalFlags) || (byrefNativeReturn && IsCLRToNative(m_dwMarshalFlags))) + if (IsHresultSwap(dwMarshalFlags)) { LocalDesc extraParamType = nativeType; extraParamType.MakeByRef(); m_pcsMarshal->SetStubTargetArgType(&extraParamType, false); - if (IsHresultSwap(dwMarshalFlags)) - { - // HRESULT swapping: the original return value is transformed into an extra - // byref parameter and the target is expected to return an HRESULT - m_pcsMarshal->SetStubTargetReturnType(ELEMENT_TYPE_I4); // native method returns an HRESULT - } - else - { - // byref structure return: the original return value is transformed into an - // extra byref parameter and the target is not expected to return anything - // - // note: we do this only for forward calls because [unmanaged calling conv. - // uses byref return] implies [managed calling conv. uses byref return] - m_pcsMarshal->SetStubTargetReturnType(ELEMENT_TYPE_VOID); - } + // HRESULT swapping: the original return value is transformed into an extra + // byref parameter and the target is expected to return an HRESULT + m_pcsMarshal->SetStubTargetReturnType(ELEMENT_TYPE_I4); // native method returns an HRESULT } else { - if (typ != ELEMENT_TYPE_VOID) - { - // small structure return: the original return value is transformed into - // ELEMENT_TYPE_U1, ELEMENT_TYPE_U2, ELEMENT_TYPE_U4, or ELEMENT_TYPE_U8 - m_pcsMarshal->SetStubTargetReturnType(typ); - } - else - { - m_pcsMarshal->SetStubTargetReturnType(&nativeType); - } + m_pcsMarshal->SetStubTargetReturnType(&nativeType); } m_managedHome.InitHome(ILStubMarshalHome::HomeType_ILLocal, m_pcsMarshal->NewLocal(managedType)); @@ -657,31 +616,14 @@ class ILMarshaler if (IsCLRToNative(dwMarshalFlags)) { - if (IsHresultSwap(dwMarshalFlags) || byrefNativeReturn) + if (IsHresultSwap(dwMarshalFlags)) { EmitReInitNative(m_pcsMarshal); EmitLoadNativeHomeAddrForByRefDispatch(pcsDispatch); // load up the byref native type as an extra arg } else { - if (typ != ELEMENT_TYPE_VOID) - { - // small structure forward: the returned integer is memcpy'd into native home - // of the structure - - DWORD dwTempLocalNum = m_pcsUnmarshal->NewLocal(typ); - m_pcsUnmarshal->EmitSTLOC(dwTempLocalNum); - - // cpblk - m_nativeHome.EmitLoadHomeAddr(m_pcsUnmarshal); - m_pcsUnmarshal->EmitLDLOCA(dwTempLocalNum); - m_pcsUnmarshal->EmitLDC(nativeSize); - m_pcsUnmarshal->EmitCPBLK(); - } - else - { - EmitStoreNativeValue(m_pcsUnmarshal); - } + EmitStoreNativeValue(m_pcsUnmarshal); } if (NeedsMarshalCleanupIndex()) @@ -737,25 +679,7 @@ class ILMarshaler } else { - if (typ != ELEMENT_TYPE_VOID) - { - // small structure return (reverse): native home of the structure is memcpy'd - // into the integer to be returned from the stub - - DWORD dwTempLocalNum = m_pcsUnmarshal->NewLocal(typ); - - // cpblk - m_pcsUnmarshal->EmitLDLOCA(dwTempLocalNum); - m_nativeHome.EmitLoadHomeAddr(m_pcsUnmarshal); - m_pcsUnmarshal->EmitLDC(nativeSize); - m_pcsUnmarshal->EmitCPBLK(); - - m_pcsUnmarshal->EmitLDLOC(dwTempLocalNum); - } - else - { - EmitLoadNativeValue(m_pcsUnmarshal); - } + EmitLoadNativeValue(m_pcsUnmarshal); } // make sure we free (and zero) the return value if an exception is thrown From 5324b7e32427efd027f9be9c38fe3ea4a3d5215a Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 14 Jul 2020 10:56:17 -0700 Subject: [PATCH 18/73] Apply format patch. --- src/coreclr/src/jit/compiler.cpp | 4 ++-- src/coreclr/src/jit/compiler.h | 6 ++++-- src/coreclr/src/jit/importer.cpp | 11 ++++++----- src/coreclr/src/jit/lclvars.cpp | 15 ++++++++++----- src/coreclr/src/jit/morph.cpp | 6 +++--- 5 files changed, 25 insertions(+), 17 deletions(-) diff --git a/src/coreclr/src/jit/compiler.cpp b/src/coreclr/src/jit/compiler.cpp index a4074c89110013..ff95744af015b4 100644 --- a/src/coreclr/src/jit/compiler.cpp +++ b/src/coreclr/src/jit/compiler.cpp @@ -1085,7 +1085,7 @@ var_types Compiler::getReturnTypeForStruct(CORINFO_CLASS_HANDLE clsHnd, useType = TYP_STRUCT; } else - { + { // Otherwise we return this struct using a return buffer // setup wbPassType and useType indicate that this is returned using a return buffer register // (reference to a return buffer) @@ -2230,7 +2230,7 @@ void Compiler::compSetProcessor() #elif defined(TARGET_ARM64) info.genCPU = CPU_ARM64; #elif defined(TARGET_AMD64) - info.genCPU = CPU_X64; + info.genCPU = CPU_X64; #elif defined(TARGET_X86) if (jitFlags.IsSet(JitFlags::JIT_FLAG_TARGET_P4)) info.genCPU = CPU_X86_PENTIUM_4; diff --git a/src/coreclr/src/jit/compiler.h b/src/coreclr/src/jit/compiler.h index 0c73b33ec53357..34125712a21479 100644 --- a/src/coreclr/src/jit/compiler.h +++ b/src/coreclr/src/jit/compiler.h @@ -9171,7 +9171,8 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #if FEATURE_MULTIREG_RET #if defined(TARGET_X86) // On x86, 64-bit longs are returned in multiple registers - return varTypeIsLong(info.compRetNativeType) || (varTypeIsStruct(info.compRetNativeType) && (info.compRetBuffArg == BAD_VAR_NUM)); + return varTypeIsLong(info.compRetNativeType) || + (varTypeIsStruct(info.compRetNativeType) && (info.compRetBuffArg == BAD_VAR_NUM)); #else // targets: X64-UNIX, ARM64 or ARM32 // On all other targets that support multireg return values: // Methods returning a struct in multiple registers have a return value of TYP_STRUCT. @@ -9196,7 +9197,8 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #if FEATURE_MULTIREG_RET #if defined(TARGET_X86) // On x86, 64-bit longs are returned in multiple registers - return varTypeIsLong(info.compRetNativeType) || (varTypeIsStruct(info.compRetNativeType) && (info.compRetBuffArg == BAD_VAR_NUM)); + return varTypeIsLong(info.compRetNativeType) || + (varTypeIsStruct(info.compRetNativeType) && (info.compRetBuffArg == BAD_VAR_NUM)); #else // targets: X64-UNIX, ARM64 or ARM32 #if defined(TARGET_ARM64) // TYP_SIMD* are returned in one register. diff --git a/src/coreclr/src/jit/importer.cpp b/src/coreclr/src/jit/importer.cpp index 24ba2e10eca1ca..b15a95b340cf2d 100644 --- a/src/coreclr/src/jit/importer.cpp +++ b/src/coreclr/src/jit/importer.cpp @@ -1186,7 +1186,7 @@ GenTree* Compiler::impAssignStructPtr(GenTree* destAddr, GenTreeCall* srcCall = src->AsCall(); if (srcCall->TreatAsHasRetBufArg(this)) { - // Case of call returning a struct via hidden retbuf arg +// Case of call returning a struct via hidden retbuf arg #if defined(TARGET_WINDOWS) && !defined(TARGET_ARM) // Unmanaged instance methods on Windows need the retbuf arg after the first (this) parameter @@ -1210,7 +1210,8 @@ GenTree* Compiler::impAssignStructPtr(GenTree* destAddr, } else { - for(; lastArg->GetNext() != nullptr; lastArg = lastArg->GetNext()); + for (; lastArg->GetNext() != nullptr; lastArg = lastArg->GetNext()) + ; lastArg->SetNext(gtPrependNewCallArg(destAddr, lastArg->GetNext())); } #else @@ -4607,7 +4608,7 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method) #if defined(TARGET_XARCH) platformNamespaceName = ".X86"; #elif defined(TARGET_ARM64) - platformNamespaceName = ".Arm"; + platformNamespaceName = ".Arm"; #else #error Unsupported platform #endif @@ -8952,7 +8953,7 @@ bool Compiler::impMethodInfo_hasRetBuffArg(CORINFO_METHOD_INFO* methInfo) if ((corType == CORINFO_TYPE_VALUECLASS) || (corType == CORINFO_TYPE_REFANY)) { - // We have some kind of STRUCT being returned +// We have some kind of STRUCT being returned #if defined(TARGET_WINDOWS) && !defined(TARGET_ARM) if (compMethodIsNativeInstanceMethod(methInfo)) { @@ -9127,7 +9128,7 @@ GenTree* Compiler::impFixupCallStructReturn(GenTreeCall* call, CORINFO_CLASS_HAN if (call->gtCallMoreFlags & GTF_CALL_M_UNMGD_INST_CALL) { howToReturnStruct = SPK_ByReference; - returnType = TYP_UNKNOWN; + returnType = TYP_UNKNOWN; } else #endif diff --git a/src/coreclr/src/jit/lclvars.cpp b/src/coreclr/src/jit/lclvars.cpp index f839fe96b76526..5b18e89699c020 100644 --- a/src/coreclr/src/jit/lclvars.cpp +++ b/src/coreclr/src/jit/lclvars.cpp @@ -349,7 +349,7 @@ void Compiler::lvaInitArgs(InitVarDscInfo* varDscInfo) lvaInitThisPtr(varDscInfo); unsigned numUserArgsToSkip = 0; - unsigned numUserArgs = info.compMethodInfo->args.numArgs; + unsigned numUserArgs = info.compMethodInfo->args.numArgs; #if defined(TARGET_WINDOWS) && !defined(TARGET_ARM) if (compMethodIsNativeInstanceMethod(info.compMethodInfo)) { @@ -597,7 +597,10 @@ void Compiler::lvaInitUserArgs(InitVarDscInfo* varDscInfo, unsigned skipArgs, un regMaskTP doubleAlignMask = RBM_NONE; #endif // TARGET_ARM - for (unsigned i = 0; i < skipArgs; i++, argLst = info.compCompHnd->getArgNext(argLst)); + for (unsigned i = 0; i < skipArgs; i++, argLst = info.compCompHnd->getArgNext(argLst)) + { + ; + } for (unsigned i = 0; i < numUserArgs; i++, varDscInfo->varNum++, varDscInfo->varDsc++, argLst = info.compCompHnd->getArgNext(argLst)) @@ -5242,8 +5245,7 @@ void Compiler::lvaAssignVirtualFrameOffsetsToArgs() { noway_assert(lvaTable[lclNum].lvIsRegArg); #ifndef TARGET_X86 - argOffs = - lvaAssignVirtualFrameOffsetToArg(lclNum, REGSIZE_BYTES, argOffs); + argOffs = lvaAssignVirtualFrameOffsetToArg(lclNum, REGSIZE_BYTES, argOffs); #endif // TARGET_X86 lclNum++; userArgsToSkip++; @@ -5285,7 +5287,10 @@ void Compiler::lvaAssignVirtualFrameOffsetsToArgs() unsigned argSigLen = info.compMethodInfo->args.numArgs; assert(userArgsToSkip <= argSigLen); argSigLen -= userArgsToSkip; - for(unsigned i = 0; i < userArgsToSkip; i++, argLst = info.compCompHnd->getArgNext(argLst)); + for (unsigned i = 0; i < userArgsToSkip; i++, argLst = info.compCompHnd->getArgNext(argLst)) + { + ; + } #ifdef TARGET_ARM // diff --git a/src/coreclr/src/jit/morph.cpp b/src/coreclr/src/jit/morph.cpp index 088d538e7d0219..0c7e059a8e0496 100644 --- a/src/coreclr/src/jit/morph.cpp +++ b/src/coreclr/src/jit/morph.cpp @@ -2776,9 +2776,9 @@ void Compiler::fgInitArgInfo(GenTreeCall* call) maxRegArgs = 0; } #ifdef UNIX_X86_ABI - // Add in the ret buff arg - if (callHasRetBuffArg) - maxRegArgs++; + // Add in the ret buff arg + if (callHasRetBuffArg) + maxRegArgs++; #endif } #endif // TARGET_X86 From 3c60ca25685877b0b646bb9c2136dd2d3875ff20 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 14 Jul 2020 11:12:11 -0700 Subject: [PATCH 19/73] Don't check IsUmanagedValueTypeReturnedByRef unless the methodtable is a value type. Use a type handle instead of a method table. Don't check size for enums. --- src/coreclr/src/vm/dllimport.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/coreclr/src/vm/dllimport.cpp b/src/coreclr/src/vm/dllimport.cpp index dc01561160c2ea..a723fb1a77063b 100644 --- a/src/coreclr/src/vm/dllimport.cpp +++ b/src/coreclr/src/vm/dllimport.cpp @@ -3634,13 +3634,13 @@ static void CreateNDirectStubWorker(StubState* pss, else { // This is an ordinary value type - see if it is returned by-ref. - MethodTable *pRetMT = msig.GetRetTypeHandleThrowing().AsMethodTable(); - if (IsUnmanagedValueTypeReturnedByRef(pRetMT->GetNativeSize())) + 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) + else if (fThisCall && !retType.IsEnum()) { nativeStackSize += sizeof(LPVOID); } From 0a6790b5440e1eeb3b626071587615970f4e2c49 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 16 Jul 2020 14:32:03 -0700 Subject: [PATCH 20/73] Fix HasRetBuffArg check for ARM and ARM64 --- src/coreclr/src/vm/callingconvention.h | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/coreclr/src/vm/callingconvention.h b/src/coreclr/src/vm/callingconvention.h index c03fcd3c12ea4b..722e32b3831e58 100644 --- a/src/coreclr/src/vm/callingconvention.h +++ b/src/coreclr/src/vm/callingconvention.h @@ -1490,7 +1490,7 @@ void ArgIteratorTemplate::ComputeReturnFlags() break; case ELEMENT_TYPE_VALUETYPE: -#ifdef ENREGISTERED_RETURNTYPE_MAXSIZE +#ifdef ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE { _ASSERTE(!thValueType.IsNull()); @@ -1548,13 +1548,17 @@ void ArgIteratorTemplate::ComputeReturnFlags() flags |= RETURN_HAS_RET_BUFFER; break; } -#endif - + if (size <= ENREGISTERED_RETURNTYPE_MAXSIZE) break; +#else + + if (size <= ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE) + break; +#endif // defined(TARGET_X86) || defined(TARGET_AMD64) #endif // UNIX_AMD64_ABI } -#endif // ENREGISTERED_RETURNTYPE_MAXSIZE +#endif // ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE // Value types are returned using return buffer by default flags |= RETURN_HAS_RET_BUFFER; From 47553e5661aeeef2992d825b03fa27b86b09aadf Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 30 Sep 2020 15:08:18 -0700 Subject: [PATCH 21/73] Pass calling convention to getReturnTypeForStruct. --- src/coreclr/src/jit/codegencommon.cpp | 4 +- src/coreclr/src/jit/compiler.cpp | 12 ++++-- src/coreclr/src/jit/compiler.h | 35 ++++++++++----- src/coreclr/src/jit/compiler.hpp | 12 +++++- src/coreclr/src/jit/flowgraph.cpp | 6 ++- src/coreclr/src/jit/gcencode.cpp | 2 +- src/coreclr/src/jit/gentree.cpp | 3 +- src/coreclr/src/jit/gentree.h | 2 + src/coreclr/src/jit/importer.cpp | 61 +++++++++++++++------------ src/coreclr/src/jit/lclvars.cpp | 2 +- src/coreclr/src/jit/lower.cpp | 2 +- src/coreclr/src/jit/morph.cpp | 10 +++-- 12 files changed, 96 insertions(+), 55 deletions(-) diff --git a/src/coreclr/src/jit/codegencommon.cpp b/src/coreclr/src/jit/codegencommon.cpp index 5923e6512f406e..969204b2f86ba5 100644 --- a/src/coreclr/src/jit/codegencommon.cpp +++ b/src/coreclr/src/jit/codegencommon.cpp @@ -10060,7 +10060,7 @@ void CodeGen::genVzeroupperIfNeeded(bool check256bitOnly /* = true*/) // Return Value: // true if type is returned in multiple registers, false otherwise. // -bool Compiler::IsMultiRegReturnedType(CORINFO_CLASS_HANDLE hClass) +bool Compiler::IsMultiRegReturnedType(CORINFO_CLASS_HANDLE hClass, CorInfoUnmanagedCallConv callConv) { if (hClass == NO_CLASS_HANDLE) { @@ -10068,7 +10068,7 @@ bool Compiler::IsMultiRegReturnedType(CORINFO_CLASS_HANDLE hClass) } structPassingKind howToReturnStruct; - var_types returnType = getReturnTypeForStruct(hClass, &howToReturnStruct); + var_types returnType = getReturnTypeForStruct(hClass, callConv, &howToReturnStruct); #ifdef TARGET_ARM64 return (varTypeIsStruct(returnType) && (howToReturnStruct != SPK_PrimitiveType)); diff --git a/src/coreclr/src/jit/compiler.cpp b/src/coreclr/src/jit/compiler.cpp index 916a12e25485e3..4fb126929dc664 100644 --- a/src/coreclr/src/jit/compiler.cpp +++ b/src/coreclr/src/jit/compiler.cpp @@ -910,9 +910,10 @@ var_types Compiler::getArgTypeForStruct(CORINFO_CLASS_HANDLE clsHnd, // Whenever this method's return value is TYP_STRUCT it always means // that multiple registers are used to return this struct. // -var_types Compiler::getReturnTypeForStruct(CORINFO_CLASS_HANDLE clsHnd, - structPassingKind* wbReturnStruct /* = nullptr */, - unsigned structSize /* = 0 */) +var_types Compiler::getReturnTypeForStruct(CORINFO_CLASS_HANDLE clsHnd, + CorInfoUnmanagedCallConv callConv, + structPassingKind* wbReturnStruct /* = nullptr */, + unsigned structSize /* = 0 */) { var_types useType = TYP_UNKNOWN; structPassingKind howToReturnStruct = SPK_Unknown; // We must change this before we return @@ -2000,6 +2001,11 @@ bool Compiler::compMethodIsNativeInstanceMethod(CORINFO_METHOD_INFO* mthInfo) return mthInfo->args.getCallConv() == CORINFO_CALLCONV_THISCALL; } +CorInfoUnmanagedCallConv Compiler::compMethodInfoGetUnmanagedCallConv(CORINFO_METHOD_INFO* mthInfo) +{ + return (CorInfoUnmanagedCallConv)mthInfo->args.getCallConv(); +} + #ifdef DEBUG static bool DidComponentUnitTests = false; diff --git a/src/coreclr/src/jit/compiler.h b/src/coreclr/src/jit/compiler.h index 9e6ba0d9855eef..c3226c8f0e1144 100644 --- a/src/coreclr/src/jit/compiler.h +++ b/src/coreclr/src/jit/compiler.h @@ -2121,7 +2121,7 @@ class Compiler #endif #if FEATURE_MULTIREG_RET - GenTree* impAssignMultiRegTypeToVar(GenTree* op, CORINFO_CLASS_HANDLE hClass); + GenTree* impAssignMultiRegTypeToVar(GenTree* op, CORINFO_CLASS_HANDLE hClass DEBUGARG(CorInfoUnmanagedCallConv callConv)); #endif // FEATURE_MULTIREG_RET GenTree* impAssignSmallStructTypeToVar(GenTree* op, CORINFO_CLASS_HANDLE hClass); @@ -2148,7 +2148,7 @@ class Compiler var_types GetHfaType(CORINFO_CLASS_HANDLE hClass); unsigned GetHfaCount(CORINFO_CLASS_HANDLE hClass); - bool IsMultiRegReturnedType(CORINFO_CLASS_HANDLE hClass); + bool IsMultiRegReturnedType(CORINFO_CLASS_HANDLE hClass, CorInfoUnmanagedCallConv callConv); //------------------------------------------------------------------------- // The following is used for validating format of EH table @@ -3705,7 +3705,7 @@ class Compiler GenTree* impFixupCallStructReturn(GenTreeCall* call, CORINFO_CLASS_HANDLE retClsHnd); - GenTree* impFixupStructReturnType(GenTree* op, CORINFO_CLASS_HANDLE retClsHnd); + GenTree* impFixupStructReturnType(GenTree* op, CORINFO_CLASS_HANDLE retClsHnd, CorInfoUnmanagedCallConv unmgdCallConv); #ifdef DEBUG var_types impImportJitTestLabelMark(int numArgs); @@ -3938,7 +3938,12 @@ class Compiler GenTree* impOptimizeCastClassOrIsInst(GenTree* op1, CORINFO_RESOLVED_TOKEN* pResolvedToken, bool isCastClass); bool VarTypeIsMultiByteAndCanEnreg( - var_types type, CORINFO_CLASS_HANDLE typeClass, unsigned* typeSize, bool forReturn, bool isVarArg); + var_types type, + CORINFO_CLASS_HANDLE typeClass, + unsigned* typeSize, + bool forReturn, + bool isVarArg, + CorInfoUnmanagedCallConv callConv); bool IsIntrinsicImplementedByUserCall(NamedIntrinsic intrinsicName); bool IsTargetIntrinsic(NamedIntrinsic intrinsicName); @@ -4242,10 +4247,12 @@ class Compiler bool exactContextNeedsRuntimeLookup, CORINFO_CALL_INFO* callInfo); - bool impTailCallRetTypeCompatible(var_types callerRetType, - CORINFO_CLASS_HANDLE callerRetTypeClass, - var_types calleeRetType, - CORINFO_CLASS_HANDLE calleeRetTypeClass); + bool impTailCallRetTypeCompatible(var_types callerRetType, + CORINFO_CLASS_HANDLE callerRetTypeClass, + CorInfoUnmanagedCallConv callerCallConv, + var_types calleeRetType, + CORINFO_CLASS_HANDLE calleeRetTypeClass, + CorInfoUnmanagedCallConv calleeCallConv); bool impIsTailCallILPattern( bool tailPrefixed, OPCODE curOpcode, const BYTE* codeAddrOfNextOpcode, const BYTE* codeEnd, bool isRecursive); @@ -4912,9 +4919,10 @@ class Compiler // Get the type that is used to return values of the given struct type. // If the size is unknown, pass 0 and it will be determined from 'clsHnd'. - var_types getReturnTypeForStruct(CORINFO_CLASS_HANDLE clsHnd, - structPassingKind* wbPassStruct = nullptr, - unsigned structSize = 0); + var_types getReturnTypeForStruct(CORINFO_CLASS_HANDLE clsHnd, + CorInfoUnmanagedCallConv callConv, + structPassingKind* wbPassStruct = nullptr, + unsigned structSize = 0); #ifdef DEBUG // Print a representation of "vnp" or "vn" on standard output. @@ -9386,6 +9394,11 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX // (only used on applicable platforms) bool compMethodIsNativeInstanceMethod(CORINFO_METHOD_INFO* mthInfo); + // Gets the unmanaged calling convention the method's entry point should have. + // Returns CorInfoUnmanagedCallConv::CORINFO_UNMANAGED_CALLCONV_UNKNOWN for the managed + // calling convention. + CorInfoUnmanagedCallConv compMethodInfoGetUnmanagedCallConv(CORINFO_METHOD_INFO* mthInfo); + #ifdef DEBUG // Components used by the compiler may write unit test suites, and // have them run within this method. They will be run only once per process, and only diff --git a/src/coreclr/src/jit/compiler.hpp b/src/coreclr/src/jit/compiler.hpp index ca3ed3c6fc668b..da296428552f7c 100644 --- a/src/coreclr/src/jit/compiler.hpp +++ b/src/coreclr/src/jit/compiler.hpp @@ -692,9 +692,17 @@ inline bool isRegParamType(var_types type) // isVarArg - whether or not this is a vararg fixed arg or variable argument // - if so on arm64 windows getArgTypeForStruct will ignore HFA // - types +// callConv - the unmanaged calling convention of the call, +// - or CORINFO_UNMANAGED_CALLCONV_UNKNOWN for the +// - managed calling convention. // inline bool Compiler::VarTypeIsMultiByteAndCanEnreg( - var_types type, CORINFO_CLASS_HANDLE typeClass, unsigned* typeSize, bool forReturn, bool isVarArg) + var_types type, + CORINFO_CLASS_HANDLE typeClass, + unsigned* typeSize, + bool forReturn, + bool isVarArg, + CorInfoUnmanagedCallConv callConv) { bool result = false; unsigned size = 0; @@ -706,7 +714,7 @@ inline bool Compiler::VarTypeIsMultiByteAndCanEnreg( if (forReturn) { structPassingKind howToReturnStruct; - type = getReturnTypeForStruct(typeClass, &howToReturnStruct, size); + type = getReturnTypeForStruct(typeClass, callConv, &howToReturnStruct, size); } else { diff --git a/src/coreclr/src/jit/flowgraph.cpp b/src/coreclr/src/jit/flowgraph.cpp index a52ba8d2847b3f..bef7711858a3a5 100644 --- a/src/coreclr/src/jit/flowgraph.cpp +++ b/src/coreclr/src/jit/flowgraph.cpp @@ -22909,7 +22909,7 @@ Compiler::fgWalkResult Compiler::fgUpdateInlineReturnExpressionPlaceHolder(GenTr if (retClsHnd != NO_CLASS_HANDLE) { structPassingKind howToReturnStruct; - var_types returnType = comp->getReturnTypeForStruct(retClsHnd, &howToReturnStruct); + var_types returnType = comp->getReturnTypeForStruct(retClsHnd, CORINFO_UNMANAGED_CALLCONV_UNKNOWN, &howToReturnStruct); GenTree* parent = data->parent; switch (howToReturnStruct) @@ -23023,7 +23023,9 @@ Compiler::fgWalkResult Compiler::fgUpdateInlineReturnExpressionPlaceHolder(GenTr GenTree* effectiveValue = value->gtEffectiveVal(/*commaOnly*/ true); noway_assert(!varTypeIsStruct(effectiveValue) || (effectiveValue->OperGet() != GT_RET_EXPR) || - !comp->IsMultiRegReturnedType(effectiveValue->AsRetExpr()->gtRetClsHnd)); + !comp->IsMultiRegReturnedType( + effectiveValue->AsRetExpr()->gtRetClsHnd, + CORINFO_UNMANAGED_CALLCONV_UNKNOWN)); } } diff --git a/src/coreclr/src/jit/gcencode.cpp b/src/coreclr/src/jit/gcencode.cpp index 6cbe702524282d..b48840a324fc93 100644 --- a/src/coreclr/src/jit/gcencode.cpp +++ b/src/coreclr/src/jit/gcencode.cpp @@ -50,7 +50,7 @@ ReturnKind GCInfo::getReturnKind() case TYP_STRUCT: { CORINFO_CLASS_HANDLE structType = compiler->info.compMethodInfo->args.retTypeClass; - var_types retType = compiler->getReturnTypeForStruct(structType); + var_types retType = compiler->getReturnTypeForStruct(structType, compiler->compMethodInfoGetUnmanagedCallConv(compiler->info.compMethodInfo)); switch (retType) { diff --git a/src/coreclr/src/jit/gentree.cpp b/src/coreclr/src/jit/gentree.cpp index 6369d66e2f32f4..f6480dc7f569da 100644 --- a/src/coreclr/src/jit/gentree.cpp +++ b/src/coreclr/src/jit/gentree.cpp @@ -6240,6 +6240,7 @@ GenTreeCall* Compiler::gtNewCallNode( node->gtRetClsHnd = nullptr; node->gtControlExpr = nullptr; node->gtCallMoreFlags = 0; + node->unmgdCallConv = (CorInfoUnmanagedCallConv)0; if (callType == CT_INDIRECT) { @@ -19176,7 +19177,7 @@ void ReturnTypeDesc::InitializeStructReturnType(Compiler* comp, CORINFO_CLASS_HA unsigned structSize = comp->info.compCompHnd->getClassSize(retClsHnd); Compiler::structPassingKind howToReturnStruct; - var_types returnType = comp->getReturnTypeForStruct(retClsHnd, &howToReturnStruct, structSize); + var_types returnType = comp->getReturnTypeForStruct(retClsHnd, comp->compMethodInfoGetUnmanagedCallConv(comp->info.compMethodInfo), &howToReturnStruct, structSize); switch (howToReturnStruct) { diff --git a/src/coreclr/src/jit/gentree.h b/src/coreclr/src/jit/gentree.h index c4b7f316ae6de3..0dc9421eedaa7a 100644 --- a/src/coreclr/src/jit/gentree.h +++ b/src/coreclr/src/jit/gentree.h @@ -4509,6 +4509,8 @@ struct GenTreeCall final : public GenTree unsigned char gtCallType : 3; // value from the gtCallTypes enumeration unsigned char gtReturnType : 5; // exact return type + CorInfoUnmanagedCallConv unmgdCallConv; + CORINFO_CLASS_HANDLE gtRetClsHnd; // The return type handle of the call if it is a struct; always available union { diff --git a/src/coreclr/src/jit/importer.cpp b/src/coreclr/src/jit/importer.cpp index 03dc41e9047c4a..baea3c16401c8e 100644 --- a/src/coreclr/src/jit/importer.cpp +++ b/src/coreclr/src/jit/importer.cpp @@ -6953,6 +6953,7 @@ void Compiler::impCheckForPInvokeCall( JITLOG((LL_INFO1000000, "\nInline a CALLI PINVOKE call from method %s", info.compFullName)); call->gtFlags |= GTF_CALL_UNMANAGED; + call->unmgdCallConv = unmanagedCallConv; if (!call->IsSuppressGCTransition()) { info.compUnmanagedCallCountWithGCTransition++; @@ -7505,10 +7506,12 @@ void Compiler::impInsertHelperCall(CORINFO_HELPER_DESC* helperInfo) // so that callee can be tail called. Note that here we don't check // compatibility in IL Verifier sense, but on the lines of return type // sizes are equal and get returned in the same return register. -bool Compiler::impTailCallRetTypeCompatible(var_types callerRetType, - CORINFO_CLASS_HANDLE callerRetTypeClass, - var_types calleeRetType, - CORINFO_CLASS_HANDLE calleeRetTypeClass) +bool Compiler::impTailCallRetTypeCompatible(var_types callerRetType, + CORINFO_CLASS_HANDLE callerRetTypeClass, + CorInfoUnmanagedCallConv callerCallConv, + var_types calleeRetType, + CORINFO_CLASS_HANDLE calleeRetTypeClass, + CorInfoUnmanagedCallConv calleeCallConv) { // Note that we can not relax this condition with genActualType() as the // calling convention dictates that the caller of a function with a small @@ -7546,9 +7549,9 @@ bool Compiler::impTailCallRetTypeCompatible(var_types callerRetType, unsigned callerRetTypeSize = 0; unsigned calleeRetTypeSize = 0; bool isCallerRetTypMBEnreg = - VarTypeIsMultiByteAndCanEnreg(callerRetType, callerRetTypeClass, &callerRetTypeSize, true, info.compIsVarArgs); + VarTypeIsMultiByteAndCanEnreg(callerRetType, callerRetTypeClass, &callerRetTypeSize, true, info.compIsVarArgs, callerCallConv); bool isCalleeRetTypMBEnreg = - VarTypeIsMultiByteAndCanEnreg(calleeRetType, calleeRetTypeClass, &calleeRetTypeSize, true, info.compIsVarArgs); + VarTypeIsMultiByteAndCanEnreg(calleeRetType, calleeRetTypeClass, &calleeRetTypeSize, true, info.compIsVarArgs, calleeCallConv); if (varTypeIsIntegral(callerRetType) || isCallerRetTypMBEnreg) { @@ -8744,8 +8747,12 @@ 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, callRetTyp, - sig->retTypeClass)) + !impTailCallRetTypeCompatible(info.compRetType, + info.compMethodInfo->args.retTypeClass, + compMethodInfoGetUnmanagedCallConv(info.compMethodInfo), + callRetTyp, + sig->retTypeClass, + call->AsCall()->unmgdCallConv)) { canTailCall = false; szCanTailCallFailReason = "Return types are not tail call compatible"; @@ -9104,7 +9111,7 @@ bool Compiler::impMethodInfo_hasRetBuffArg(CORINFO_METHOD_INFO* methInfo) structPassingKind howToReturnStruct = SPK_Unknown; - var_types returnType = getReturnTypeForStruct(methInfo->args.retTypeClass, &howToReturnStruct); + var_types returnType = getReturnTypeForStruct(methInfo->args.retTypeClass, compMethodInfoGetUnmanagedCallConv(methInfo), &howToReturnStruct); if (howToReturnStruct == SPK_ByReference) { @@ -9252,7 +9259,7 @@ GenTree* Compiler::impFixupCallStructReturn(GenTreeCall* call, CORINFO_CLASS_HAN // No need to assign a multi-reg struct to a local var if: // - It is a tail call or // - The call is marked for in-lining later - return impAssignMultiRegTypeToVar(call, retClsHnd); + return impAssignMultiRegTypeToVar(call, retClsHnd DEBUGARG(call->unmgdCallConv)); } } @@ -9274,7 +9281,7 @@ GenTree* Compiler::impFixupCallStructReturn(GenTreeCall* call, CORINFO_CLASS_HAN else #endif { - returnType = getReturnTypeForStruct(retClsHnd, &howToReturnStruct); + returnType = getReturnTypeForStruct(retClsHnd, call->unmgdCallConv, &howToReturnStruct); } if (howToReturnStruct == SPK_ByReference) @@ -9341,7 +9348,7 @@ GenTree* Compiler::impFixupCallStructReturn(GenTreeCall* call, CORINFO_CLASS_HAN // No need to assign a multi-reg struct to a local var if: // - It is a tail call or // - The call is marked for in-lining later - return impAssignMultiRegTypeToVar(call, retClsHnd); + return impAssignMultiRegTypeToVar(call, retClsHnd DEBUGARG(call->unmgdCallConv)); } } #endif // FEATURE_MULTIREG_RET @@ -9358,7 +9365,7 @@ GenTree* Compiler::impFixupCallStructReturn(GenTreeCall* call, CORINFO_CLASS_HAN Note that this method is only call for !TARGET_X86 */ -GenTree* Compiler::impFixupStructReturnType(GenTree* op, CORINFO_CLASS_HANDLE retClsHnd) +GenTree* Compiler::impFixupStructReturnType(GenTree* op, CORINFO_CLASS_HANDLE retClsHnd, CorInfoUnmanagedCallConv unmgdCallConv) { assert(varTypeIsStruct(info.compRetType)); assert(info.compRetBuffArg == BAD_VAR_NUM); @@ -9373,7 +9380,7 @@ GenTree* Compiler::impFixupStructReturnType(GenTree* op, CORINFO_CLASS_HANDLE re assert(!info.compIsVarArgs); // Is method returning a multi-reg struct? - if (varTypeIsStruct(info.compRetNativeType) && IsMultiRegReturnedType(retClsHnd)) + if (varTypeIsStruct(info.compRetNativeType) && IsMultiRegReturnedType(retClsHnd, unmgdCallConv)) { // In case of multi-reg struct return, we force IR to be one of the following: // GT_RETURN(lclvar) or GT_RETURN(call). If op is anything other than a @@ -9396,11 +9403,11 @@ GenTree* Compiler::impFixupStructReturnType(GenTree* op, CORINFO_CLASS_HANDLE re return op; } - return impAssignMultiRegTypeToVar(op, retClsHnd); + return impAssignMultiRegTypeToVar(op, retClsHnd DEBUGARG(unmgdCallConv)); } #elif defined(TARGET_X86) // Is method returning a multi-reg struct? - if (varTypeIsStruct(info.compRetNativeType) && IsMultiRegReturnedType(retClsHnd)) + if (varTypeIsStruct(info.compRetNativeType) && IsMultiRegReturnedType(retClsHnd, unmgdCallConv)) { // In case of multi-reg struct return, we force IR to be one of the following: // GT_RETURN(lclvar) or GT_RETURN(call). If op is anything other than a @@ -9423,7 +9430,7 @@ GenTree* Compiler::impFixupStructReturnType(GenTree* op, CORINFO_CLASS_HANDLE re return op; } - return impAssignMultiRegTypeToVar(op, retClsHnd); + return impAssignMultiRegTypeToVar(op, retClsHnd DEBUGARG(unmgdCallConv)); } #else // !UNIX_AMD64_ABI assert(info.compRetNativeType != TYP_STRUCT); @@ -9460,13 +9467,13 @@ GenTree* Compiler::impFixupStructReturnType(GenTree* op, CORINFO_CLASS_HANDLE re return op; } } - return impAssignMultiRegTypeToVar(op, retClsHnd); + return impAssignMultiRegTypeToVar(op, retClsHnd DEBUGARG(unmgdCallConv)); } #elif FEATURE_MULTIREG_RET && defined(TARGET_ARM64) // Is method returning a multi-reg struct? - if (IsMultiRegReturnedType(retClsHnd)) + if (IsMultiRegReturnedType(retClsHnd, unmgdCallConv)) { if (op->gtOper == GT_LCL_VAR) { @@ -9499,7 +9506,7 @@ GenTree* Compiler::impFixupStructReturnType(GenTree* op, CORINFO_CLASS_HANDLE re return op; } } - return impAssignMultiRegTypeToVar(op, retClsHnd); + return impAssignMultiRegTypeToVar(op, retClsHnd DEBUGARG(unmgdCallConv)); } #endif // FEATURE_MULTIREG_RET && FEATURE_HFA @@ -9585,7 +9592,7 @@ GenTree* Compiler::impFixupStructReturnType(GenTree* op, CORINFO_CLASS_HANDLE re } else if (op->gtOper == GT_COMMA) { - op->AsOp()->gtOp2 = impFixupStructReturnType(op->AsOp()->gtOp2, retClsHnd); + op->AsOp()->gtOp2 = impFixupStructReturnType(op->AsOp()->gtOp2, retClsHnd, unmgdCallConv); } op->gtType = info.compRetNativeType; @@ -13399,7 +13406,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) // Calls with large struct return value have to go through this. // Helper calls with small struct return value also have to go // through this since they do not follow Unix calling convention. - if (op1->gtOper != GT_CALL || !IsMultiRegReturnedType(clsHnd) || + if (op1->gtOper != GT_CALL || !IsMultiRegReturnedType(clsHnd, op1->AsCall()->unmgdCallConv) || op1->AsCall()->gtCallType == CT_HELPER) #endif // UNIX_AMD64_ABI { @@ -15792,7 +15799,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) #if FEATURE_MULTIREG_RET - if (varTypeIsStruct(op1) && IsMultiRegReturnedType(resolvedToken.hClass)) + if (varTypeIsStruct(op1) && IsMultiRegReturnedType(resolvedToken.hClass, CORINFO_UNMANAGED_CALLCONV_UNKNOWN)) { // Unbox nullable helper returns a TYP_STRUCT. // For the multi-reg case we need to spill it to a temp so that @@ -16661,7 +16668,7 @@ GenTree* Compiler::impAssignSmallStructTypeToVar(GenTree* op, CORINFO_CLASS_HAND // Returns: // Tree with reference to struct local to use as call return value. -GenTree* Compiler::impAssignMultiRegTypeToVar(GenTree* op, CORINFO_CLASS_HANDLE hClass) +GenTree* Compiler::impAssignMultiRegTypeToVar(GenTree* op, CORINFO_CLASS_HANDLE hClass DEBUGARG(CorInfoUnmanagedCallConv callConv)) { unsigned tmpNum = lvaGrabTemp(true DEBUGARG("Return value temp for multireg return")); impAssignTempGen(tmpNum, op, hClass, (unsigned)CHECK_SPILL_ALL); @@ -16670,7 +16677,7 @@ GenTree* Compiler::impAssignMultiRegTypeToVar(GenTree* op, CORINFO_CLASS_HANDLE // TODO-1stClassStructs: Handle constant propagation and CSE-ing of multireg returns. ret->gtFlags |= GTF_DONT_CSE; - assert(IsMultiRegReturnedType(hClass)); + assert(IsMultiRegReturnedType(hClass, callConv)); // Mark the var so that fields are not promoted and stay together. lvaTable[tmpNum].lvIsMultiRegRet = true; @@ -16824,7 +16831,7 @@ 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); + op2 = impFixupStructReturnType(op2, retClsHnd, compMethodInfoGetUnmanagedCallConv(info.compMethodInfo)); } else { @@ -17182,7 +17189,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); + op2 = impFixupStructReturnType(op2, retClsHnd, compMethodInfoGetUnmanagedCallConv(info.compMethodInfo)); // return op2 var_types returnType; if (compDoOldStructRetyping()) diff --git a/src/coreclr/src/jit/lclvars.cpp b/src/coreclr/src/jit/lclvars.cpp index f2d782704a9459..5741113f09c6c7 100644 --- a/src/coreclr/src/jit/lclvars.cpp +++ b/src/coreclr/src/jit/lclvars.cpp @@ -145,7 +145,7 @@ void Compiler::lvaInitTypeRef() CORINFO_CLASS_HANDLE retClsHnd = info.compMethodInfo->args.retTypeClass; Compiler::structPassingKind howToReturnStruct; - var_types returnType = getReturnTypeForStruct(retClsHnd, &howToReturnStruct); + var_types returnType = getReturnTypeForStruct(retClsHnd, compMethodInfoGetUnmanagedCallConv(info.compMethodInfo), &howToReturnStruct); // We can safely widen the return type for enclosed structs. if ((howToReturnStruct == SPK_PrimitiveType) || (howToReturnStruct == SPK_EnclosingType)) diff --git a/src/coreclr/src/jit/lower.cpp b/src/coreclr/src/jit/lower.cpp index 2867a7bacbb46d..4977eeb3200446 100644 --- a/src/coreclr/src/jit/lower.cpp +++ b/src/coreclr/src/jit/lower.cpp @@ -3452,7 +3452,7 @@ void Lowering::LowerCallStruct(GenTreeCall* call) assert(!comp->compDoOldStructRetyping()); CORINFO_CLASS_HANDLE retClsHnd = call->gtRetClsHnd; Compiler::structPassingKind howToReturnStruct; - var_types returnType = comp->getReturnTypeForStruct(retClsHnd, &howToReturnStruct); + var_types returnType = comp->getReturnTypeForStruct(retClsHnd, call->unmgdCallConv, &howToReturnStruct); assert(returnType != TYP_STRUCT && returnType != TYP_UNKNOWN); var_types origType = call->TypeGet(); call->gtType = genActualType(returnType); diff --git a/src/coreclr/src/jit/morph.cpp b/src/coreclr/src/jit/morph.cpp index ab6f1f657a9b5c..2144f5b7b1f576 100644 --- a/src/coreclr/src/jit/morph.cpp +++ b/src/coreclr/src/jit/morph.cpp @@ -5012,7 +5012,7 @@ void Compiler::fgFixupStructReturn(GenTree* callNode) } else { - returnType = getReturnTypeForStruct(retClsHnd, &howToReturnStruct); + returnType = getReturnTypeForStruct(retClsHnd, call->unmgdCallConv, &howToReturnStruct); } if (howToReturnStruct == SPK_ByReference) @@ -6699,7 +6699,9 @@ bool Compiler::fgCanFastTailCall(GenTreeCall* callee, const char** failReason) { var_types retType = (compDoOldStructRetyping() ? info.compRetNativeType : info.compRetType); assert(impTailCallRetTypeCompatible(retType, info.compMethodInfo->args.retTypeClass, - (var_types)callee->gtReturnType, callee->gtRetClsHnd)); + compMethodInfoGetUnmanagedCallConv(info.compMethodInfo), + (var_types)callee->gtReturnType, callee->gtRetClsHnd, + callee->unmgdCallConv)); } #endif @@ -7620,7 +7622,7 @@ GenTree* Compiler::fgMorphPotentialTailCall(GenTreeCall* call) { CORINFO_CLASS_HANDLE retClsHnd = call->gtRetClsHnd; Compiler::structPassingKind howToReturnStruct; - callType = getReturnTypeForStruct(retClsHnd, &howToReturnStruct); + callType = getReturnTypeForStruct(retClsHnd, call->unmgdCallConv, &howToReturnStruct); assert((howToReturnStruct != SPK_Unknown) && (howToReturnStruct != SPK_ByReference)); if (howToReturnStruct == SPK_ByValue) { @@ -7983,7 +7985,7 @@ GenTree* Compiler::fgCreateCallDispatcherAndGetResult(GenTreeCall* orig if (varTypeIsStruct(origCall->gtType)) { - retVal = impFixupStructReturnType(retVal, origCall->gtRetClsHnd); + retVal = impFixupStructReturnType(retVal, origCall->gtRetClsHnd, origCall->unmgdCallConv); } } else From 4b8870bff7d67f5f0732392e63c7ba401122718d Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 1 Oct 2020 10:56:56 -0700 Subject: [PATCH 22/73] Propogate calling convention through the JIT so it can determine struct return type based on calling convention. Update runtime side to not assume that the JIT enregisters 8-byte struct returns on Windows x86 (since it doesn't). --- src/coreclr/src/jit/codegencommon.cpp | 4 +-- src/coreclr/src/jit/codegenxarch.cpp | 2 +- src/coreclr/src/jit/compiler.cpp | 31 +++++++++++++++++++--- src/coreclr/src/jit/gentree.cpp | 6 ++--- src/coreclr/src/jit/gentree.h | 6 ++--- src/coreclr/src/jit/importer.cpp | 33 ++++++------------------ src/coreclr/src/jit/lower.cpp | 2 +- src/coreclr/src/jit/lsrabuild.cpp | 2 +- src/coreclr/src/vm/callingconvention.h | 6 +---- src/coreclr/src/vm/dllimportcallback.cpp | 8 +++--- 10 files changed, 50 insertions(+), 50 deletions(-) diff --git a/src/coreclr/src/jit/codegencommon.cpp b/src/coreclr/src/jit/codegencommon.cpp index 10393800c9d5d9..4c76612538db08 100644 --- a/src/coreclr/src/jit/codegencommon.cpp +++ b/src/coreclr/src/jit/codegencommon.cpp @@ -11475,7 +11475,7 @@ void CodeGen::genReturn(GenTree* treeNode) } else // we must have a struct return type { - retTypeDesc.InitializeStructReturnType(compiler, compiler->info.compMethodInfo->args.retTypeClass); + retTypeDesc.InitializeStructReturnType(compiler, compiler->info.compMethodInfo->args.retTypeClass, compiler->compMethodInfoGetUnmanagedCallConv(compiler->info.compMethodInfo)); } regCount = retTypeDesc.GetReturnRegCount(); } @@ -11600,7 +11600,7 @@ void CodeGen::genStructReturn(GenTree* treeNode) if (actualOp1->OperIs(GT_LCL_VAR)) { varDsc = compiler->lvaGetDesc(actualOp1->AsLclVar()->GetLclNum()); - retTypeDesc.InitializeStructReturnType(compiler, varDsc->GetStructHnd()); + retTypeDesc.InitializeStructReturnType(compiler, varDsc->GetStructHnd(), compiler->compMethodInfoGetUnmanagedCallConv(compiler->info.compMethodInfo)); assert(varDsc->lvIsMultiRegRet); } else diff --git a/src/coreclr/src/jit/codegenxarch.cpp b/src/coreclr/src/jit/codegenxarch.cpp index 3bcda89f437d15..33189d9cd98b56 100644 --- a/src/coreclr/src/jit/codegenxarch.cpp +++ b/src/coreclr/src/jit/codegenxarch.cpp @@ -140,7 +140,7 @@ void CodeGen::genEmitGSCookieCheck(bool pushReg) } else // we must have a struct return type { - retTypeDesc.InitializeStructReturnType(compiler, compiler->info.compMethodInfo->args.retTypeClass); + retTypeDesc.InitializeStructReturnType(compiler, compiler->info.compMethodInfo->args.retTypeClass, compiler->compMethodInfoGetUnmanagedCallConv(compiler->info.compMethodInfo)); } const unsigned regCount = retTypeDesc.GetReturnRegCount(); diff --git a/src/coreclr/src/jit/compiler.cpp b/src/coreclr/src/jit/compiler.cpp index 52b0307eb5204a..080a68d82504f8 100644 --- a/src/coreclr/src/jit/compiler.cpp +++ b/src/coreclr/src/jit/compiler.cpp @@ -956,7 +956,19 @@ var_types Compiler::getReturnTypeForStruct(CORINFO_CLASS_HANDLE clsHnd, #endif // UNIX_AMD64_ABI #ifdef UNIX_X86_ABI - canReturnInRegister = false; + if (callConv != CORINFO_UNMANAGED_CALLCONV_UNKNOWN) + { + canReturnInRegister = false; + howToReturnStruct = SPK_ByReference; + useType = TYP_UNKNOWN; + } +#elif defined(TARGET_WINDOWS) && !defined(TARGET_ARM) + if (callConv == CORINFO_UNMANAGED_CALLCONV_THISCALL) + { + canReturnInRegister = false; + howToReturnStruct = SPK_ByReference; + useType = TYP_UNKNOWN; + } #endif // Check for cases where a small struct is returned in a register @@ -1078,8 +1090,14 @@ var_types Compiler::getReturnTypeForStruct(CORINFO_CLASS_HANDLE clsHnd, } #elif defined(TARGET_X86) - // Only 8-byte structs are return in multiple registers - if (structSize == MAX_RET_MULTIREG_BYTES) + // Only 8-byte structs are return in multiple registers. + // We also only support multireg struct returns + // on x86 to match the native calling convention. + // So only do it for native calling conventions that aren't + // instance methods + if (structSize == MAX_RET_MULTIREG_BYTES && + callConv != CORINFO_UNMANAGED_CALLCONV_UNKNOWN && + canReturnInRegister) { // setup wbPassType and useType indicate that this is return by value in multiple registers howToReturnStruct = SPK_ByValue; @@ -2003,7 +2021,12 @@ bool Compiler::compMethodIsNativeInstanceMethod(CORINFO_METHOD_INFO* mthInfo) CorInfoUnmanagedCallConv Compiler::compMethodInfoGetUnmanagedCallConv(CORINFO_METHOD_INFO* mthInfo) { - return (CorInfoUnmanagedCallConv)mthInfo->args.getCallConv(); + CorInfoCallConv callConv = mthInfo->args.getCallConv(); + if (callConv == CORINFO_CALLCONV_DEFAULT || callConv == CORINFO_CALLCONV_VARARG) + { + return CORINFO_UNMANAGED_CALLCONV_UNKNOWN; + } + return (CorInfoUnmanagedCallConv)callConv; } #ifdef DEBUG diff --git a/src/coreclr/src/jit/gentree.cpp b/src/coreclr/src/jit/gentree.cpp index 0e635f2c1fe37d..b41a68059d8e6d 100644 --- a/src/coreclr/src/jit/gentree.cpp +++ b/src/coreclr/src/jit/gentree.cpp @@ -15709,7 +15709,7 @@ GenTree* Compiler::gtNewRefCOMfield(GenTree* objPtr, #if FEATURE_MULTIREG_RET if (varTypeIsStruct(call)) { - call->InitializeStructReturnType(this, structType); + call->InitializeStructReturnType(this, structType, call->unmgdCallConv); } #endif // FEATURE_MULTIREG_RET @@ -19206,7 +19206,7 @@ GenTree* Compiler::gtNewMustThrowException(unsigned helper, var_types type, CORI // Return Value // None // -void ReturnTypeDesc::InitializeStructReturnType(Compiler* comp, CORINFO_CLASS_HANDLE retClsHnd) +void ReturnTypeDesc::InitializeStructReturnType(Compiler* comp, CORINFO_CLASS_HANDLE retClsHnd, CorInfoUnmanagedCallConv callConv) { assert(!m_inited); @@ -19216,7 +19216,7 @@ void ReturnTypeDesc::InitializeStructReturnType(Compiler* comp, CORINFO_CLASS_HA unsigned structSize = comp->info.compCompHnd->getClassSize(retClsHnd); Compiler::structPassingKind howToReturnStruct; - var_types returnType = comp->getReturnTypeForStruct(retClsHnd, comp->compMethodInfoGetUnmanagedCallConv(comp->info.compMethodInfo), &howToReturnStruct, structSize); + var_types returnType = comp->getReturnTypeForStruct(retClsHnd, callConv, &howToReturnStruct, structSize); switch (howToReturnStruct) { diff --git a/src/coreclr/src/jit/gentree.h b/src/coreclr/src/jit/gentree.h index bd469b520c8f44..9011864b663be7 100644 --- a/src/coreclr/src/jit/gentree.h +++ b/src/coreclr/src/jit/gentree.h @@ -3634,7 +3634,7 @@ struct ReturnTypeDesc } // Initialize the Return Type Descriptor for a method that returns a struct type - void InitializeStructReturnType(Compiler* comp, CORINFO_CLASS_HANDLE retClsHnd); + void InitializeStructReturnType(Compiler* comp, CORINFO_CLASS_HANDLE retClsHnd, CorInfoUnmanagedCallConv callConv); // Initialize the Return Type Descriptor for a method that returns a TYP_LONG // Only needed for X86 and arm32. @@ -3994,10 +3994,10 @@ struct GenTreeCall final : public GenTree #endif } - void InitializeStructReturnType(Compiler* comp, CORINFO_CLASS_HANDLE retClsHnd) + void InitializeStructReturnType(Compiler* comp, CORINFO_CLASS_HANDLE retClsHnd, CorInfoUnmanagedCallConv callConv) { #if FEATURE_MULTIREG_RET - gtReturnTypeDesc.InitializeStructReturnType(comp, retClsHnd); + gtReturnTypeDesc.InitializeStructReturnType(comp, retClsHnd, callConv); #endif } diff --git a/src/coreclr/src/jit/importer.cpp b/src/coreclr/src/jit/importer.cpp index 0ea874f068fda4..98f7d2cc21244d 100644 --- a/src/coreclr/src/jit/importer.cpp +++ b/src/coreclr/src/jit/importer.cpp @@ -9091,14 +9091,7 @@ bool Compiler::impMethodInfo_hasRetBuffArg(CORINFO_METHOD_INFO* methInfo) if ((corType == CORINFO_TYPE_VALUECLASS) || (corType == CORINFO_TYPE_REFANY)) { -// We have some kind of STRUCT being returned -#if defined(TARGET_WINDOWS) && !defined(TARGET_ARM) - if (compMethodIsNativeInstanceMethod(methInfo)) - { - return true; - } -#endif - + // We have some kind of STRUCT being returned structPassingKind howToReturnStruct = SPK_Unknown; var_types returnType = getReturnTypeForStruct(methInfo->args.retTypeClass, compMethodInfoGetUnmanagedCallConv(methInfo), &howToReturnStruct); @@ -9192,7 +9185,7 @@ GenTree* Compiler::impFixupCallStructReturn(GenTreeCall* call, CORINFO_CLASS_HAN call->gtRetClsHnd = retClsHnd; #if FEATURE_MULTIREG_RET - call->InitializeStructReturnType(this, retClsHnd); + call->InitializeStructReturnType(this, retClsHnd, call->unmgdCallConv); #endif // FEATURE_MULTIREG_RET #ifdef UNIX_AMD64_ABI @@ -9262,17 +9255,7 @@ GenTree* Compiler::impFixupCallStructReturn(GenTreeCall* call, CORINFO_CLASS_HAN structPassingKind howToReturnStruct; var_types returnType; -#if defined(TARGET_WINDOWS) && !defined(TARGET_ARM) - if (call->gtCallMoreFlags & GTF_CALL_M_UNMGD_INST_CALL) - { - howToReturnStruct = SPK_ByReference; - returnType = TYP_UNKNOWN; - } - else -#endif - { - returnType = getReturnTypeForStruct(retClsHnd, call->unmgdCallConv, &howToReturnStruct); - } + returnType = getReturnTypeForStruct(retClsHnd, call->unmgdCallConv, &howToReturnStruct); if (howToReturnStruct == SPK_ByReference) { @@ -15489,7 +15472,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) { op1->AsCall()->gtRetClsHnd = classHandle; #if FEATURE_MULTIREG_RET - op1->AsCall()->InitializeStructReturnType(this, classHandle); + op1->AsCall()->InitializeStructReturnType(this, classHandle, op1->AsCall()->unmgdCallConv); #endif } @@ -15536,7 +15519,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) if (!compDoOldStructRetyping()) { #if FEATURE_MULTIREG_RET - op1->AsCall()->InitializeStructReturnType(this, tokenType); + op1->AsCall()->InitializeStructReturnType(this, tokenType, op1->AsCall()->unmgdCallConv); #endif op1->AsCall()->gtRetClsHnd = tokenType; } @@ -17028,7 +17011,7 @@ 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); + retTypeDesc.InitializeStructReturnType(this, retClsHnd, compMethodInfoGetUnmanagedCallConv(info.compMethodInfo)); unsigned retRegCount = retTypeDesc.GetReturnRegCount(); if (retRegCount != 0) @@ -17062,7 +17045,7 @@ bool Compiler::impReturnInstruction(int prefixFlags, OPCODE& opcode) else #elif defined(TARGET_ARM64) ReturnTypeDesc retTypeDesc; - retTypeDesc.InitializeStructReturnType(this, retClsHnd); + retTypeDesc.InitializeStructReturnType(this, retClsHnd, compMethodInfoGetUnmanagedCallConv(info.compMethodInfo)); unsigned retRegCount = retTypeDesc.GetReturnRegCount(); if (retRegCount != 0) @@ -17086,7 +17069,7 @@ bool Compiler::impReturnInstruction(int prefixFlags, OPCODE& opcode) else #elif defined(TARGET_X86) ReturnTypeDesc retTypeDesc; - retTypeDesc.InitializeStructReturnType(this, retClsHnd); + retTypeDesc.InitializeStructReturnType(this, retClsHnd, compMethodInfoGetUnmanagedCallConv(info.compMethodInfo)); unsigned retRegCount = retTypeDesc.GetReturnRegCount(); if (retRegCount != 0) diff --git a/src/coreclr/src/jit/lower.cpp b/src/coreclr/src/jit/lower.cpp index 32c72163fb0f4a..384272bdb4c674 100644 --- a/src/coreclr/src/jit/lower.cpp +++ b/src/coreclr/src/jit/lower.cpp @@ -2991,7 +2991,7 @@ void Lowering::LowerRet(GenTreeUnOp* ret) ReturnTypeDesc retTypeDesc; LclVarDsc* varDsc = nullptr; varDsc = comp->lvaGetDesc(retVal->AsLclVar()->GetLclNum()); - retTypeDesc.InitializeStructReturnType(comp, varDsc->GetStructHnd()); + retTypeDesc.InitializeStructReturnType(comp, varDsc->GetStructHnd(), comp->compMethodInfoGetUnmanagedCallConv(comp->info.compMethodInfo)); if (retTypeDesc.GetReturnRegCount() > 1) { CheckMultiRegLclVar(retVal->AsLclVar(), &retTypeDesc); diff --git a/src/coreclr/src/jit/lsrabuild.cpp b/src/coreclr/src/jit/lsrabuild.cpp index 146a9565376307..e6f1fb15c23048 100644 --- a/src/coreclr/src/jit/lsrabuild.cpp +++ b/src/coreclr/src/jit/lsrabuild.cpp @@ -3475,7 +3475,7 @@ int LinearScan::BuildReturn(GenTree* tree) assert(compiler->lvaEnregMultiRegVars); LclVarDsc* varDsc = compiler->lvaGetDesc(op1->AsLclVar()->GetLclNum()); ReturnTypeDesc retTypeDesc; - retTypeDesc.InitializeStructReturnType(compiler, varDsc->GetStructHnd()); + retTypeDesc.InitializeStructReturnType(compiler, varDsc->GetStructHnd(), compiler->compMethodInfoGetUnmanagedCallConv(compiler->info.compMethodInfo)); pRetTypeDesc = &retTypeDesc; assert(compiler->lvaGetDesc(op1->AsLclVar()->GetLclNum())->lvFieldCnt == retTypeDesc.GetReturnRegCount()); diff --git a/src/coreclr/src/vm/callingconvention.h b/src/coreclr/src/vm/callingconvention.h index 722e32b3831e58..9dd05f1931d392 100644 --- a/src/coreclr/src/vm/callingconvention.h +++ b/src/coreclr/src/vm/callingconvention.h @@ -1548,14 +1548,10 @@ void ArgIteratorTemplate::ComputeReturnFlags() flags |= RETURN_HAS_RET_BUFFER; break; } - - if (size <= ENREGISTERED_RETURNTYPE_MAXSIZE) - break; -#else +#endif // defined(TARGET_X86) || defined(TARGET_AMD64) if (size <= ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE) break; -#endif // defined(TARGET_X86) || defined(TARGET_AMD64) #endif // UNIX_AMD64_ABI } #endif // ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE diff --git a/src/coreclr/src/vm/dllimportcallback.cpp b/src/coreclr/src/vm/dllimportcallback.cpp index 6c6464c1e4dbbb..bb766b4e53f6f0 100644 --- a/src/coreclr/src/vm/dllimportcallback.cpp +++ b/src/coreclr/src/vm/dllimportcallback.cpp @@ -476,13 +476,11 @@ VOID UMEntryThunk::CompileUMThunkWorker(UMThunkStubInfo *pInfo, { pcpusl->X86EmitPushReg(kEDI); // Save EDI register // Move the return value from the enregisterd return from the JIT - // to the return buffer that the C++ compiler expects. + // to the return buffer that the native calling convention expects. + // NOTE: Since the managed calling convention does not enregister 8-byte + // struct returns on x86, we only need to handle the single-register 4-byte case. pcpusl->X86EmitIndexRegLoad(kEDI, kEBX, retbufofs); pcpusl->X86EmitIndexRegStore(kEDI, 0x0, kEAX); - if (pInfo->m_wFlags & umtmlMultiregRetVal) - { - pcpusl->X86EmitIndexRegStore(kEDI, 0x4 /* skip EAX half of the return value */, kEDX); - } pcpusl->X86EmitPopReg(kEDI); // Restore EDI register } // pretend that the method returned the ret buf hidden argument From 6c56bc823b6103d1a69b10594b42d4ed105ca5da Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 1 Oct 2020 10:58:30 -0700 Subject: [PATCH 23/73] Remove now-unused code for multireg struct returns in the x86 reverse P/Invoke stub linker. Signed-off-by: Jeremy Koritzinsky --- src/coreclr/src/vm/callingconvention.h | 20 -------------------- src/coreclr/src/vm/dllimportcallback.cpp | 4 ---- src/coreclr/src/vm/dllimportcallback.h | 1 - 3 files changed, 25 deletions(-) diff --git a/src/coreclr/src/vm/callingconvention.h b/src/coreclr/src/vm/callingconvention.h index 9dd05f1931d392..334eca186a98b0 100644 --- a/src/coreclr/src/vm/callingconvention.h +++ b/src/coreclr/src/vm/callingconvention.h @@ -1841,26 +1841,6 @@ class ArgIterator : public ArgIteratorTemplate CorElementType type = m_pSig->GetReturnTypeNormalized(&thValueType); return type == ELEMENT_TYPE_VALUETYPE && !thValueType.IsEnum(); } - -#if defined(TARGET_X86) && !defined(FEATURE_STUBS_AS_IL) - BOOL HasMultiRegReturnStruct() - { - WRAPPER_NO_CONTRACT; - - TypeHandle thValueType; - CorElementType type = m_pSig->GetReturnTypeNormalized(&thValueType); - if (type != ELEMENT_TYPE_VALUETYPE || thValueType.IsEnum()) - { - return FALSE; - } - unsigned int structSize = thValueType.GetSize(); - if (structSize == ENREGISTERED_RETURNTYPE_MAXSIZE) - { - return TRUE; - } - return FALSE; - } -#endif }; // Conventience helper diff --git a/src/coreclr/src/vm/dllimportcallback.cpp b/src/coreclr/src/vm/dllimportcallback.cpp index bb766b4e53f6f0..42afe93b8e3307 100644 --- a/src/coreclr/src/vm/dllimportcallback.cpp +++ b/src/coreclr/src/vm/dllimportcallback.cpp @@ -894,10 +894,6 @@ Stub *UMThunkMarshInfo::CompileNExportThunk(LoaderHeap *pLoaderHeap, PInvokeStat else if (argit.HasValueTypeReturn()) { stubInfo.m_wFlags |= umtmlThisCallHiddenArg | umtmlEnregRetValToBuf; - if (argit.HasMultiRegReturnStruct()) - { - stubInfo.m_wFlags |= umtmlMultiregRetVal; - } } } } diff --git a/src/coreclr/src/vm/dllimportcallback.h b/src/coreclr/src/vm/dllimportcallback.h index 42bf180537df22..5c6124a53e1c62 100644 --- a/src/coreclr/src/vm/dllimportcallback.h +++ b/src/coreclr/src/vm/dllimportcallback.h @@ -23,7 +23,6 @@ enum UMThunkStubFlags umtmlThisCallHiddenArg = 0x0004, umtmlFpu = 0x0008, umtmlEnregRetValToBuf = 0x0010, - umtmlMultiregRetVal = 0x0020, #ifdef TARGET_X86 // the signature is trivial so stub need not be generated and the target can be called directly umtmlSkipStub = 0x0080, From 3897473efd9b813fef712add88891879b8119cfc Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 2 Oct 2020 14:49:11 -0700 Subject: [PATCH 24/73] Treat RuntimeArgumentHandle and RuntimeMethodHandleInternal as ELEMENT_TYPE_I when jitting on all platforms. When I removed the struct normalization code from x86, I inadvertantly broke treating these types as ELEMENT_TYPE_I. On all platforms other than x86, this was handled at an earlier point in the code. Remove the if-def and make this happen on all platforms. --- src/coreclr/src/vm/methodtablebuilder.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/coreclr/src/vm/methodtablebuilder.cpp b/src/coreclr/src/vm/methodtablebuilder.cpp index f5942caae87e83..beae7a34f38692 100644 --- a/src/coreclr/src/vm/methodtablebuilder.cpp +++ b/src/coreclr/src/vm/methodtablebuilder.cpp @@ -9718,7 +9718,6 @@ void MethodTableBuilder::CheckForSystemTypes() { pMT->SetIsNullable(); } -#ifndef TARGET_X86 else if (strcmp(name, g_RuntimeArgumentHandleName) == 0) { pMT->SetInternalCorElementType (ELEMENT_TYPE_I); @@ -9727,7 +9726,6 @@ void MethodTableBuilder::CheckForSystemTypes() { pMT->SetInternalCorElementType (ELEMENT_TYPE_I); } -#endif } else { From 426b0f3d20848fc6dfabdc54c4e7c18a2513a5ca Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 2 Oct 2020 17:18:57 -0700 Subject: [PATCH 25/73] Treat RuntimeFieldHandleInternal as an ELEMENT_TYPE_I since it is used similarly to RuntimeMethodHandleInternal across the FCall boundary --- src/coreclr/src/vm/classnames.h | 1 + src/coreclr/src/vm/methodtablebuilder.cpp | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/src/coreclr/src/vm/classnames.h b/src/coreclr/src/vm/classnames.h index 0766ff27765230..b3a7288733bc5a 100644 --- a/src/coreclr/src/vm/classnames.h +++ b/src/coreclr/src/vm/classnames.h @@ -82,6 +82,7 @@ #define g_ReflectionReflectItfName "System.Reflection.IReflect" #define g_RuntimeArgumentHandleName "RuntimeArgumentHandle" #define g_RuntimeFieldHandleClassName "System.RuntimeFieldHandle" +#define g_RuntimeFieldHandleInternalName "RuntimeFieldHandleInternal" #define g_RuntimeMethodHandleClassName "System.RuntimeMethodHandle" #define g_RuntimeMethodHandleInternalName "RuntimeMethodHandleInternal" #define g_RuntimeTypeHandleClassName "System.RuntimeTypeHandle" diff --git a/src/coreclr/src/vm/methodtablebuilder.cpp b/src/coreclr/src/vm/methodtablebuilder.cpp index beae7a34f38692..b35adf5b4e393b 100644 --- a/src/coreclr/src/vm/methodtablebuilder.cpp +++ b/src/coreclr/src/vm/methodtablebuilder.cpp @@ -9726,6 +9726,10 @@ void MethodTableBuilder::CheckForSystemTypes() { pMT->SetInternalCorElementType (ELEMENT_TYPE_I); } + else if (strcmp(name, g_RuntimeFieldHandleInternalName) == 0) + { + pMT->SetInternalCorElementType (ELEMENT_TYPE_I); + } } else { From cc73336ecf8afd28445144f3d13a155e47df9e5c Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 2 Oct 2020 17:22:59 -0700 Subject: [PATCH 26/73] Apply format patch. Signed-off-by: Jeremy Koritzinsky --- src/coreclr/src/jit/codegencommon.cpp | 8 +++-- src/coreclr/src/jit/codegenxarch.cpp | 4 ++- src/coreclr/src/jit/compiler.cpp | 11 +++---- src/coreclr/src/jit/compiler.h | 20 ++++++------ src/coreclr/src/jit/compiler.hpp | 13 ++++---- src/coreclr/src/jit/flowgraph.cpp | 10 +++--- src/coreclr/src/jit/gcencode.cpp | 4 ++- src/coreclr/src/jit/gentree.cpp | 8 +++-- src/coreclr/src/jit/importer.cpp | 44 +++++++++++++++------------ src/coreclr/src/jit/lclvars.cpp | 4 ++- src/coreclr/src/jit/lower.cpp | 5 +-- src/coreclr/src/jit/lsrabuild.cpp | 4 ++- 12 files changed, 78 insertions(+), 57 deletions(-) diff --git a/src/coreclr/src/jit/codegencommon.cpp b/src/coreclr/src/jit/codegencommon.cpp index 4c76612538db08..634508bfdbac1d 100644 --- a/src/coreclr/src/jit/codegencommon.cpp +++ b/src/coreclr/src/jit/codegencommon.cpp @@ -11475,7 +11475,9 @@ void CodeGen::genReturn(GenTree* treeNode) } else // we must have a struct return type { - retTypeDesc.InitializeStructReturnType(compiler, compiler->info.compMethodInfo->args.retTypeClass, compiler->compMethodInfoGetUnmanagedCallConv(compiler->info.compMethodInfo)); + retTypeDesc.InitializeStructReturnType(compiler, compiler->info.compMethodInfo->args.retTypeClass, + compiler->compMethodInfoGetUnmanagedCallConv( + compiler->info.compMethodInfo)); } regCount = retTypeDesc.GetReturnRegCount(); } @@ -11600,7 +11602,9 @@ void CodeGen::genStructReturn(GenTree* treeNode) if (actualOp1->OperIs(GT_LCL_VAR)) { varDsc = compiler->lvaGetDesc(actualOp1->AsLclVar()->GetLclNum()); - retTypeDesc.InitializeStructReturnType(compiler, varDsc->GetStructHnd(), compiler->compMethodInfoGetUnmanagedCallConv(compiler->info.compMethodInfo)); + retTypeDesc.InitializeStructReturnType(compiler, varDsc->GetStructHnd(), + compiler->compMethodInfoGetUnmanagedCallConv( + compiler->info.compMethodInfo)); assert(varDsc->lvIsMultiRegRet); } else diff --git a/src/coreclr/src/jit/codegenxarch.cpp b/src/coreclr/src/jit/codegenxarch.cpp index 33189d9cd98b56..d46e33b86ff0d3 100644 --- a/src/coreclr/src/jit/codegenxarch.cpp +++ b/src/coreclr/src/jit/codegenxarch.cpp @@ -140,7 +140,9 @@ void CodeGen::genEmitGSCookieCheck(bool pushReg) } else // we must have a struct return type { - retTypeDesc.InitializeStructReturnType(compiler, compiler->info.compMethodInfo->args.retTypeClass, compiler->compMethodInfoGetUnmanagedCallConv(compiler->info.compMethodInfo)); + retTypeDesc.InitializeStructReturnType(compiler, compiler->info.compMethodInfo->args.retTypeClass, + compiler->compMethodInfoGetUnmanagedCallConv( + compiler->info.compMethodInfo)); } const unsigned regCount = retTypeDesc.GetReturnRegCount(); diff --git a/src/coreclr/src/jit/compiler.cpp b/src/coreclr/src/jit/compiler.cpp index 080a68d82504f8..222597e2463b9a 100644 --- a/src/coreclr/src/jit/compiler.cpp +++ b/src/coreclr/src/jit/compiler.cpp @@ -959,15 +959,15 @@ var_types Compiler::getReturnTypeForStruct(CORINFO_CLASS_HANDLE clsHnd, if (callConv != CORINFO_UNMANAGED_CALLCONV_UNKNOWN) { canReturnInRegister = false; - howToReturnStruct = SPK_ByReference; - useType = TYP_UNKNOWN; + howToReturnStruct = SPK_ByReference; + useType = TYP_UNKNOWN; } #elif defined(TARGET_WINDOWS) && !defined(TARGET_ARM) if (callConv == CORINFO_UNMANAGED_CALLCONV_THISCALL) { canReturnInRegister = false; - howToReturnStruct = SPK_ByReference; - useType = TYP_UNKNOWN; + howToReturnStruct = SPK_ByReference; + useType = TYP_UNKNOWN; } #endif @@ -1095,8 +1095,7 @@ var_types Compiler::getReturnTypeForStruct(CORINFO_CLASS_HANDLE clsHnd, // on x86 to match the native calling convention. // So only do it for native calling conventions that aren't // instance methods - if (structSize == MAX_RET_MULTIREG_BYTES && - callConv != CORINFO_UNMANAGED_CALLCONV_UNKNOWN && + if (structSize == MAX_RET_MULTIREG_BYTES && callConv != CORINFO_UNMANAGED_CALLCONV_UNKNOWN && canReturnInRegister) { // setup wbPassType and useType indicate that this is return by value in multiple registers diff --git a/src/coreclr/src/jit/compiler.h b/src/coreclr/src/jit/compiler.h index cf96d7947f62fa..498119076c084c 100644 --- a/src/coreclr/src/jit/compiler.h +++ b/src/coreclr/src/jit/compiler.h @@ -2147,7 +2147,8 @@ class Compiler #endif #if FEATURE_MULTIREG_RET - GenTree* impAssignMultiRegTypeToVar(GenTree* op, CORINFO_CLASS_HANDLE hClass DEBUGARG(CorInfoUnmanagedCallConv callConv)); + GenTree* impAssignMultiRegTypeToVar(GenTree* op, + CORINFO_CLASS_HANDLE hClass DEBUGARG(CorInfoUnmanagedCallConv callConv)); #endif // FEATURE_MULTIREG_RET GenTree* impAssignSmallStructTypeToVar(GenTree* op, CORINFO_CLASS_HANDLE hClass); @@ -3731,7 +3732,9 @@ class Compiler GenTree* impFixupCallStructReturn(GenTreeCall* call, CORINFO_CLASS_HANDLE retClsHnd); - GenTree* impFixupStructReturnType(GenTree* op, CORINFO_CLASS_HANDLE retClsHnd, CorInfoUnmanagedCallConv unmgdCallConv); + GenTree* impFixupStructReturnType(GenTree* op, + CORINFO_CLASS_HANDLE retClsHnd, + CorInfoUnmanagedCallConv unmgdCallConv); #ifdef DEBUG var_types impImportJitTestLabelMark(int numArgs); @@ -3963,13 +3966,12 @@ class Compiler GenTree* impOptimizeCastClassOrIsInst(GenTree* op1, CORINFO_RESOLVED_TOKEN* pResolvedToken, bool isCastClass); - bool VarTypeIsMultiByteAndCanEnreg( - var_types type, - CORINFO_CLASS_HANDLE typeClass, - unsigned* typeSize, - bool forReturn, - bool isVarArg, - CorInfoUnmanagedCallConv callConv); + bool VarTypeIsMultiByteAndCanEnreg(var_types type, + CORINFO_CLASS_HANDLE typeClass, + unsigned* typeSize, + bool forReturn, + bool isVarArg, + CorInfoUnmanagedCallConv callConv); bool IsIntrinsicImplementedByUserCall(NamedIntrinsic intrinsicName); bool IsTargetIntrinsic(NamedIntrinsic intrinsicName); diff --git a/src/coreclr/src/jit/compiler.hpp b/src/coreclr/src/jit/compiler.hpp index c03dd8080e85ab..bbe7b29242619d 100644 --- a/src/coreclr/src/jit/compiler.hpp +++ b/src/coreclr/src/jit/compiler.hpp @@ -696,13 +696,12 @@ inline bool isRegParamType(var_types type) // - or CORINFO_UNMANAGED_CALLCONV_UNKNOWN for the // - managed calling convention. // -inline bool Compiler::VarTypeIsMultiByteAndCanEnreg( - var_types type, - CORINFO_CLASS_HANDLE typeClass, - unsigned* typeSize, - bool forReturn, - bool isVarArg, - CorInfoUnmanagedCallConv callConv) +inline bool Compiler::VarTypeIsMultiByteAndCanEnreg(var_types type, + CORINFO_CLASS_HANDLE typeClass, + unsigned* typeSize, + bool forReturn, + bool isVarArg, + CorInfoUnmanagedCallConv callConv) { bool result = false; unsigned size = 0; diff --git a/src/coreclr/src/jit/flowgraph.cpp b/src/coreclr/src/jit/flowgraph.cpp index f04a6fdde2dbae..f23b30251cdb37 100644 --- a/src/coreclr/src/jit/flowgraph.cpp +++ b/src/coreclr/src/jit/flowgraph.cpp @@ -22733,8 +22733,9 @@ Compiler::fgWalkResult Compiler::fgUpdateInlineReturnExpressionPlaceHolder(GenTr if (retClsHnd != NO_CLASS_HANDLE) { structPassingKind howToReturnStruct; - var_types returnType = comp->getReturnTypeForStruct(retClsHnd, CORINFO_UNMANAGED_CALLCONV_UNKNOWN, &howToReturnStruct); - GenTree* parent = data->parent; + var_types returnType = + comp->getReturnTypeForStruct(retClsHnd, CORINFO_UNMANAGED_CALLCONV_UNKNOWN, &howToReturnStruct); + GenTree* parent = data->parent; switch (howToReturnStruct) { @@ -22847,9 +22848,8 @@ Compiler::fgWalkResult Compiler::fgUpdateInlineReturnExpressionPlaceHolder(GenTr GenTree* effectiveValue = value->gtEffectiveVal(/*commaOnly*/ true); noway_assert(!varTypeIsStruct(effectiveValue) || (effectiveValue->OperGet() != GT_RET_EXPR) || - !comp->IsMultiRegReturnedType( - effectiveValue->AsRetExpr()->gtRetClsHnd, - CORINFO_UNMANAGED_CALLCONV_UNKNOWN)); + !comp->IsMultiRegReturnedType(effectiveValue->AsRetExpr()->gtRetClsHnd, + CORINFO_UNMANAGED_CALLCONV_UNKNOWN)); } } diff --git a/src/coreclr/src/jit/gcencode.cpp b/src/coreclr/src/jit/gcencode.cpp index b48840a324fc93..c3cacc25992280 100644 --- a/src/coreclr/src/jit/gcencode.cpp +++ b/src/coreclr/src/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->compMethodInfoGetUnmanagedCallConv(compiler->info.compMethodInfo)); + var_types retType = + compiler->getReturnTypeForStruct(structType, compiler->compMethodInfoGetUnmanagedCallConv( + compiler->info.compMethodInfo)); switch (retType) { diff --git a/src/coreclr/src/jit/gentree.cpp b/src/coreclr/src/jit/gentree.cpp index b41a68059d8e6d..c362f5d7e609f9 100644 --- a/src/coreclr/src/jit/gentree.cpp +++ b/src/coreclr/src/jit/gentree.cpp @@ -6251,7 +6251,7 @@ GenTreeCall* Compiler::gtNewCallNode( node->gtRetClsHnd = nullptr; node->gtControlExpr = nullptr; node->gtCallMoreFlags = 0; - node->unmgdCallConv = (CorInfoUnmanagedCallConv)0; + node->unmgdCallConv = (CorInfoUnmanagedCallConv)0; if (callType == CT_INDIRECT) { @@ -19206,7 +19206,9 @@ GenTree* Compiler::gtNewMustThrowException(unsigned helper, var_types type, CORI // Return Value // None // -void ReturnTypeDesc::InitializeStructReturnType(Compiler* comp, CORINFO_CLASS_HANDLE retClsHnd, CorInfoUnmanagedCallConv callConv) +void ReturnTypeDesc::InitializeStructReturnType(Compiler* comp, + CORINFO_CLASS_HANDLE retClsHnd, + CorInfoUnmanagedCallConv callConv) { assert(!m_inited); @@ -19216,7 +19218,7 @@ void ReturnTypeDesc::InitializeStructReturnType(Compiler* comp, CORINFO_CLASS_HA unsigned structSize = comp->info.compCompHnd->getClassSize(retClsHnd); Compiler::structPassingKind howToReturnStruct; - var_types returnType = comp->getReturnTypeForStruct(retClsHnd, callConv, &howToReturnStruct, structSize); + var_types returnType = comp->getReturnTypeForStruct(retClsHnd, callConv, &howToReturnStruct, structSize); switch (howToReturnStruct) { diff --git a/src/coreclr/src/jit/importer.cpp b/src/coreclr/src/jit/importer.cpp index 98f7d2cc21244d..4e416b47170b08 100644 --- a/src/coreclr/src/jit/importer.cpp +++ b/src/coreclr/src/jit/importer.cpp @@ -4737,7 +4737,7 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method) #if defined(TARGET_XARCH) platformNamespaceName = ".X86"; #elif defined(TARGET_ARM64) - platformNamespaceName = ".Arm"; + platformNamespaceName = ".Arm"; #else #error Unsupported platform #endif @@ -7538,10 +7538,10 @@ bool Compiler::impTailCallRetTypeCompatible(var_types callerRetTy // trust code can make those tail calls. unsigned callerRetTypeSize = 0; unsigned calleeRetTypeSize = 0; - bool isCallerRetTypMBEnreg = - VarTypeIsMultiByteAndCanEnreg(callerRetType, callerRetTypeClass, &callerRetTypeSize, true, info.compIsVarArgs, callerCallConv); - bool isCalleeRetTypMBEnreg = - VarTypeIsMultiByteAndCanEnreg(calleeRetType, calleeRetTypeClass, &calleeRetTypeSize, true, info.compIsVarArgs, calleeCallConv); + bool isCallerRetTypMBEnreg = VarTypeIsMultiByteAndCanEnreg(callerRetType, callerRetTypeClass, &callerRetTypeSize, + true, info.compIsVarArgs, callerCallConv); + bool isCalleeRetTypMBEnreg = VarTypeIsMultiByteAndCanEnreg(calleeRetType, calleeRetTypeClass, &calleeRetTypeSize, + true, info.compIsVarArgs, calleeCallConv); if (varTypeIsIntegral(callerRetType) || isCallerRetTypMBEnreg) { @@ -8737,12 +8737,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, - compMethodInfoGetUnmanagedCallConv(info.compMethodInfo), - callRetTyp, - sig->retTypeClass, - call->AsCall()->unmgdCallConv)) + !impTailCallRetTypeCompatible(info.compRetType, info.compMethodInfo->args.retTypeClass, + compMethodInfoGetUnmanagedCallConv(info.compMethodInfo), callRetTyp, + sig->retTypeClass, call->AsCall()->unmgdCallConv)) { canTailCall = false; szCanTailCallFailReason = "Return types are not tail call compatible"; @@ -9094,7 +9091,8 @@ bool Compiler::impMethodInfo_hasRetBuffArg(CORINFO_METHOD_INFO* methInfo) // We have some kind of STRUCT being returned structPassingKind howToReturnStruct = SPK_Unknown; - var_types returnType = getReturnTypeForStruct(methInfo->args.retTypeClass, compMethodInfoGetUnmanagedCallConv(methInfo), &howToReturnStruct); + var_types returnType = getReturnTypeForStruct(methInfo->args.retTypeClass, + compMethodInfoGetUnmanagedCallConv(methInfo), &howToReturnStruct); if (howToReturnStruct == SPK_ByReference) { @@ -9338,7 +9336,9 @@ GenTree* Compiler::impFixupCallStructReturn(GenTreeCall* call, CORINFO_CLASS_HAN Note that this method is only call for !TARGET_X86 */ -GenTree* Compiler::impFixupStructReturnType(GenTree* op, CORINFO_CLASS_HANDLE retClsHnd, CorInfoUnmanagedCallConv unmgdCallConv) +GenTree* Compiler::impFixupStructReturnType(GenTree* op, + CORINFO_CLASS_HANDLE retClsHnd, + CorInfoUnmanagedCallConv unmgdCallConv) { assert(varTypeIsStruct(info.compRetType)); assert(info.compRetBuffArg == BAD_VAR_NUM); @@ -15773,7 +15773,8 @@ void Compiler::impImportBlockCode(BasicBlock* block) #if FEATURE_MULTIREG_RET - if (varTypeIsStruct(op1) && IsMultiRegReturnedType(resolvedToken.hClass, CORINFO_UNMANAGED_CALLCONV_UNKNOWN)) + if (varTypeIsStruct(op1) && + IsMultiRegReturnedType(resolvedToken.hClass, CORINFO_UNMANAGED_CALLCONV_UNKNOWN)) { // Unbox nullable helper returns a TYP_STRUCT. // For the multi-reg case we need to spill it to a temp so that @@ -16648,7 +16649,8 @@ GenTree* Compiler::impAssignSmallStructTypeToVar(GenTree* op, CORINFO_CLASS_HAND // Returns: // Tree with reference to struct local to use as call return value. -GenTree* Compiler::impAssignMultiRegTypeToVar(GenTree* op, CORINFO_CLASS_HANDLE hClass DEBUGARG(CorInfoUnmanagedCallConv callConv)) +GenTree* Compiler::impAssignMultiRegTypeToVar(GenTree* op, + CORINFO_CLASS_HANDLE hClass DEBUGARG(CorInfoUnmanagedCallConv callConv)) { unsigned tmpNum = lvaGrabTemp(true DEBUGARG("Return value temp for multireg return")); impAssignTempGen(tmpNum, op, hClass, (unsigned)CHECK_SPILL_ALL); @@ -16811,7 +16813,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, compMethodInfoGetUnmanagedCallConv(info.compMethodInfo)); + op2 = impFixupStructReturnType(op2, retClsHnd, + compMethodInfoGetUnmanagedCallConv(info.compMethodInfo)); } else { @@ -17011,7 +17014,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, compMethodInfoGetUnmanagedCallConv(info.compMethodInfo)); + retTypeDesc.InitializeStructReturnType(this, retClsHnd, + compMethodInfoGetUnmanagedCallConv(info.compMethodInfo)); unsigned retRegCount = retTypeDesc.GetReturnRegCount(); if (retRegCount != 0) @@ -17045,7 +17049,8 @@ bool Compiler::impReturnInstruction(int prefixFlags, OPCODE& opcode) else #elif defined(TARGET_ARM64) ReturnTypeDesc retTypeDesc; - retTypeDesc.InitializeStructReturnType(this, retClsHnd, compMethodInfoGetUnmanagedCallConv(info.compMethodInfo)); + retTypeDesc.InitializeStructReturnType(this, retClsHnd, + compMethodInfoGetUnmanagedCallConv(info.compMethodInfo)); unsigned retRegCount = retTypeDesc.GetReturnRegCount(); if (retRegCount != 0) @@ -17069,7 +17074,8 @@ bool Compiler::impReturnInstruction(int prefixFlags, OPCODE& opcode) else #elif defined(TARGET_X86) ReturnTypeDesc retTypeDesc; - retTypeDesc.InitializeStructReturnType(this, retClsHnd, compMethodInfoGetUnmanagedCallConv(info.compMethodInfo)); + retTypeDesc.InitializeStructReturnType(this, retClsHnd, + compMethodInfoGetUnmanagedCallConv(info.compMethodInfo)); unsigned retRegCount = retTypeDesc.GetReturnRegCount(); if (retRegCount != 0) diff --git a/src/coreclr/src/jit/lclvars.cpp b/src/coreclr/src/jit/lclvars.cpp index 9fbdacbb419908..c3be1335e25da3 100644 --- a/src/coreclr/src/jit/lclvars.cpp +++ b/src/coreclr/src/jit/lclvars.cpp @@ -145,7 +145,9 @@ void Compiler::lvaInitTypeRef() CORINFO_CLASS_HANDLE retClsHnd = info.compMethodInfo->args.retTypeClass; Compiler::structPassingKind howToReturnStruct; - var_types returnType = getReturnTypeForStruct(retClsHnd, compMethodInfoGetUnmanagedCallConv(info.compMethodInfo), &howToReturnStruct); + var_types returnType = + getReturnTypeForStruct(retClsHnd, compMethodInfoGetUnmanagedCallConv(info.compMethodInfo), + &howToReturnStruct); // We can safely widen the return type for enclosed structs. if ((howToReturnStruct == SPK_PrimitiveType) || (howToReturnStruct == SPK_EnclosingType)) diff --git a/src/coreclr/src/jit/lower.cpp b/src/coreclr/src/jit/lower.cpp index 384272bdb4c674..2406d01ae0e5f9 100644 --- a/src/coreclr/src/jit/lower.cpp +++ b/src/coreclr/src/jit/lower.cpp @@ -2991,7 +2991,8 @@ void Lowering::LowerRet(GenTreeUnOp* ret) ReturnTypeDesc retTypeDesc; LclVarDsc* varDsc = nullptr; varDsc = comp->lvaGetDesc(retVal->AsLclVar()->GetLclNum()); - retTypeDesc.InitializeStructReturnType(comp, varDsc->GetStructHnd(), comp->compMethodInfoGetUnmanagedCallConv(comp->info.compMethodInfo)); + retTypeDesc.InitializeStructReturnType(comp, varDsc->GetStructHnd(), + comp->compMethodInfoGetUnmanagedCallConv(comp->info.compMethodInfo)); if (retTypeDesc.GetReturnRegCount() > 1) { CheckMultiRegLclVar(retVal->AsLclVar(), &retTypeDesc); @@ -3452,7 +3453,7 @@ void Lowering::LowerCallStruct(GenTreeCall* call) assert(!comp->compDoOldStructRetyping()); CORINFO_CLASS_HANDLE retClsHnd = call->gtRetClsHnd; Compiler::structPassingKind howToReturnStruct; - var_types returnType = comp->getReturnTypeForStruct(retClsHnd, call->unmgdCallConv, &howToReturnStruct); + var_types returnType = comp->getReturnTypeForStruct(retClsHnd, call->unmgdCallConv, &howToReturnStruct); assert(returnType != TYP_STRUCT && returnType != TYP_UNKNOWN); var_types origType = call->TypeGet(); call->gtType = genActualType(returnType); diff --git a/src/coreclr/src/jit/lsrabuild.cpp b/src/coreclr/src/jit/lsrabuild.cpp index e6f1fb15c23048..7e75ad57a900ac 100644 --- a/src/coreclr/src/jit/lsrabuild.cpp +++ b/src/coreclr/src/jit/lsrabuild.cpp @@ -3475,7 +3475,9 @@ int LinearScan::BuildReturn(GenTree* tree) assert(compiler->lvaEnregMultiRegVars); LclVarDsc* varDsc = compiler->lvaGetDesc(op1->AsLclVar()->GetLclNum()); ReturnTypeDesc retTypeDesc; - retTypeDesc.InitializeStructReturnType(compiler, varDsc->GetStructHnd(), compiler->compMethodInfoGetUnmanagedCallConv(compiler->info.compMethodInfo)); + retTypeDesc.InitializeStructReturnType(compiler, varDsc->GetStructHnd(), + compiler->compMethodInfoGetUnmanagedCallConv( + compiler->info.compMethodInfo)); pRetTypeDesc = &retTypeDesc; assert(compiler->lvaGetDesc(op1->AsLclVar()->GetLclNum())->lvFieldCnt == retTypeDesc.GetReturnRegCount()); From a825b8a2770917aefc0be5b52f276382985e6586 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 2 Oct 2020 17:24:18 -0700 Subject: [PATCH 27/73] Use enum value instead of 0. Signed-off-by: Jeremy Koritzinsky --- src/coreclr/src/jit/gentree.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/src/jit/gentree.cpp b/src/coreclr/src/jit/gentree.cpp index c362f5d7e609f9..9823ca7e117041 100644 --- a/src/coreclr/src/jit/gentree.cpp +++ b/src/coreclr/src/jit/gentree.cpp @@ -6251,7 +6251,7 @@ GenTreeCall* Compiler::gtNewCallNode( node->gtRetClsHnd = nullptr; node->gtControlExpr = nullptr; node->gtCallMoreFlags = 0; - node->unmgdCallConv = (CorInfoUnmanagedCallConv)0; + node->unmgdCallConv = CORINFO_UNMANAGED_CALLCONV_UNKNOWN; if (callType == CT_INDIRECT) { From 9c0cbb14980515e50cab8d1c2081e7d70991880a Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 2 Oct 2020 17:26:38 -0700 Subject: [PATCH 28/73] Update comments. Signed-off-by: Jeremy Koritzinsky --- src/coreclr/src/vm/callingconvention.h | 2 ++ src/coreclr/src/vm/dllimportcallback.cpp | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/coreclr/src/vm/callingconvention.h b/src/coreclr/src/vm/callingconvention.h index 334eca186a98b0..a7b070d153b783 100644 --- a/src/coreclr/src/vm/callingconvention.h +++ b/src/coreclr/src/vm/callingconvention.h @@ -1839,6 +1839,8 @@ class ArgIterator : public ArgIteratorTemplate TypeHandle thValueType; CorElementType type = m_pSig->GetReturnTypeNormalized(&thValueType); + // Enums are normalized to their underlying type when passing to and from functions. + // This occurs in both managed and native calling conventions. return type == ELEMENT_TYPE_VALUETYPE && !thValueType.IsEnum(); } }; diff --git a/src/coreclr/src/vm/dllimportcallback.cpp b/src/coreclr/src/vm/dllimportcallback.cpp index 42afe93b8e3307..3db95056af9138 100644 --- a/src/coreclr/src/vm/dllimportcallback.cpp +++ b/src/coreclr/src/vm/dllimportcallback.cpp @@ -475,7 +475,7 @@ VOID UMEntryThunk::CompileUMThunkWorker(UMThunkStubInfo *pInfo, if (pInfo->m_wFlags & umtmlEnregRetValToBuf) { pcpusl->X86EmitPushReg(kEDI); // Save EDI register - // Move the return value from the enregisterd return from the JIT + // Move the return value from the enregistered return from the JIT // to the return buffer that the native calling convention expects. // NOTE: Since the managed calling convention does not enregister 8-byte // struct returns on x86, we only need to handle the single-register 4-byte case. From 63507b74ee73d903ab1803f5513bb0c7fa063b49 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 5 Oct 2020 12:16:25 -0700 Subject: [PATCH 29/73] Fix stack imbalance in x86 ThisCall reverse tests where native has a return buffer but the managed calling convention doesn't. --- src/coreclr/src/vm/dllimportcallback.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/coreclr/src/vm/dllimportcallback.cpp b/src/coreclr/src/vm/dllimportcallback.cpp index 3db95056af9138..73bad18adf9868 100644 --- a/src/coreclr/src/vm/dllimportcallback.cpp +++ b/src/coreclr/src/vm/dllimportcallback.cpp @@ -894,6 +894,10 @@ Stub *UMThunkMarshInfo::CompileNExportThunk(LoaderHeap *pLoaderHeap, PInvokeStat else if (argit.HasValueTypeReturn()) { stubInfo.m_wFlags |= umtmlThisCallHiddenArg | umtmlEnregRetValToBuf; + // When the native signature hasa return buffer but the + // managed one does not, we need to handle popping the + // the return buffer of the stack manually, which we do here. + m_cbRetPop += 4; } } } From 1262c27b4679062b235d202e8bfb48cd2e3a39dd Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 7 Oct 2020 10:44:11 -0700 Subject: [PATCH 30/73] Specify source register for copy instruction so the correct instruction is emitted. --- src/coreclr/src/jit/codegencommon.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/src/jit/codegencommon.cpp b/src/coreclr/src/jit/codegencommon.cpp index 57ee82962f4d54..e49d2bc8c79787 100644 --- a/src/coreclr/src/jit/codegencommon.cpp +++ b/src/coreclr/src/jit/codegencommon.cpp @@ -11785,7 +11785,7 @@ void CodeGen::genMultiRegStoreToLocal(GenTreeLclVar* lclNode) hasRegs = true; if (varReg != reg) { - inst_RV_RV(ins_Copy(type), varReg, reg, type); + inst_RV_RV(ins_Copy(reg, type), varReg, reg, type); } fieldVarDsc->SetRegNum(varReg); } From ef2adac758284076f0f535c39c240e951a33072e Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 7 Oct 2020 12:05:28 -0700 Subject: [PATCH 31/73] Fix instance call P/Invoke's on Windows ARM64. Make better usage of canReturnInRegister to avoid a struct that has been marked as byref being marked as ByValueAsHfa. Instance call return buffers on Windows ARM64 don't use the standard return buffer register, so don't use use it in that case. --- src/coreclr/src/jit/compiler.cpp | 5 ++--- src/coreclr/src/jit/morph.cpp | 11 ++++++++++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/coreclr/src/jit/compiler.cpp b/src/coreclr/src/jit/compiler.cpp index 082b4ea27e900e..3482d71f8051d4 100644 --- a/src/coreclr/src/jit/compiler.cpp +++ b/src/coreclr/src/jit/compiler.cpp @@ -1025,7 +1025,7 @@ var_types Compiler::getReturnTypeForStruct(CORINFO_CLASS_HANDLE clsHnd, // If so, we should have already set howToReturnStruct, too. assert(howToReturnStruct != SPK_Unknown); } - else // We can't replace the struct with a "primitive" type + else if (canReturnInRegister) // We can't replace the struct with a "primitive" type { // See if we can return this struct by value, possibly in multiple registers // or if we should return it using a return buffer register @@ -1095,8 +1095,7 @@ var_types Compiler::getReturnTypeForStruct(CORINFO_CLASS_HANDLE clsHnd, // on x86 to match the native calling convention. // So only do it for native calling conventions that aren't // instance methods - if (structSize == MAX_RET_MULTIREG_BYTES && callConv != CORINFO_UNMANAGED_CALLCONV_UNKNOWN && - canReturnInRegister) + if (structSize == MAX_RET_MULTIREG_BYTES && callConv != CORINFO_UNMANAGED_CALLCONV_UNKNOWN) { // setup wbPassType and useType indicate that this is return by value in multiple registers howToReturnStruct = SPK_ByValue; diff --git a/src/coreclr/src/jit/morph.cpp b/src/coreclr/src/jit/morph.cpp index 3c42b757c94b74..853635b48277ac 100644 --- a/src/coreclr/src/jit/morph.cpp +++ b/src/coreclr/src/jit/morph.cpp @@ -2669,9 +2669,18 @@ void Compiler::fgInitArgInfo(GenTreeCall* call) // If/when we change that, the following code needs to be changed to correctly support the (TBD) managed calling // convention for x86/SSE. - // If we have a Fixed Return Buffer argument register then we setup a non-standard argument for it + // If we have a Fixed Return Buffer argument register then we setup a non-standard argument for it. // + // We don't use the fixed return buffer argument if we have the special unmanaged instance call convention. + // That convention doesn't use the fixed return buffer register. + // + CLANG_FORMAT_COMMENT_ANCHOR; + +#if defined(TARGET_WINDOWS) && !defined(TARGET_ARM) + if (hasFixedRetBuffReg() && call->HasRetBufArg() && (call->gtCallMoreFlags & GTF_CALL_M_UNMGD_INST_CALL) == 0) +#else if (hasFixedRetBuffReg() && call->HasRetBufArg()) +#endif { args = call->gtCallArgs; assert(args != nullptr); From 5c2a9bc7aa6273b341a8a86707b65644633f8e4c Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 7 Oct 2020 17:01:41 -0700 Subject: [PATCH 32/73] Fix the reverse P/Invoke case for instance methods on ARM64. Treat the return buffer as a normal arg and don't use the special retbuf register. Implicitly return the return buffer argument from the function to satisfy the C++ compiler's dependence on the implicit behavior (similar to Windows x64). --- src/coreclr/src/jit/compiler.h | 9 +++++++-- src/coreclr/src/jit/importer.cpp | 8 ++++++++ src/coreclr/src/jit/lclvars.cpp | 16 ++++++++++++---- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/coreclr/src/jit/compiler.h b/src/coreclr/src/jit/compiler.h index 5a5bb2d8a976c3..f6a3cfb2f1f383 100644 --- a/src/coreclr/src/jit/compiler.h +++ b/src/coreclr/src/jit/compiler.h @@ -3426,7 +3426,7 @@ class Compiler void lvaInitArgs(InitVarDscInfo* varDscInfo); void lvaInitThisPtr(InitVarDscInfo* varDscInfo); - void lvaInitRetBuffArg(InitVarDscInfo* varDscInfo); + void lvaInitRetBuffArg(InitVarDscInfo* varDscInfo, bool useFixedRetBufReg); void lvaInitUserArgs(InitVarDscInfo* varDscInfo, unsigned skipArgs, unsigned takeArgs); void lvaInitGenericsCtxt(InitVarDscInfo* varDscInfo); void lvaInitVarArgsHandle(InitVarDscInfo* varDscInfo); @@ -9312,12 +9312,17 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX // methods with hidden RetBufArg in RAX. In such case GT_RETURN is of TYP_BYREF, // returning the address of RetBuf. // - // 3. Windows 64-bit native calling convention also requires the address of RetBuff + // 3. Windows x64 native calling convention also requires the address of RetBuff // to be returned in RAX. + // 4. Windows ARM64 native instance calling convention requires the address of RetBuff + // to be returned in x0. CLANG_FORMAT_COMMENT_ANCHOR; #ifdef TARGET_AMD64 return (info.compRetBuffArg != BAD_VAR_NUM); +#elif defined(TARGET_WINDOWS) && defined(TARGET_ARM64) + return (compMethodIsNativeInstanceMethod(info.compMethodInfo) || + compIsProfilerHookNeeded()) && (info.compRetBuffArg != BAD_VAR_NUM); #else // !TARGET_AMD64 return (compIsProfilerHookNeeded()) && (info.compRetBuffArg != BAD_VAR_NUM); #endif // !TARGET_AMD64 diff --git a/src/coreclr/src/jit/importer.cpp b/src/coreclr/src/jit/importer.cpp index 55a14f165f7223..e4ef3b5ee5df66 100644 --- a/src/coreclr/src/jit/importer.cpp +++ b/src/coreclr/src/jit/importer.cpp @@ -17161,6 +17161,14 @@ bool Compiler::impReturnInstruction(int prefixFlags, OPCODE& opcode) { op1 = gtNewOperNode(GT_RETURN, TYP_BYREF, gtNewLclvNode(info.compRetBuffArg, TYP_BYREF)); } +#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 (compMethodIsNativeInstanceMethod(info.compMethodInfo)) + { + op1 = gtNewOperNode(GT_RETURN, TYP_BYREF, gtNewLclvNode(info.compRetBuffArg, TYP_BYREF)); + } +#endif else { // return void diff --git a/src/coreclr/src/jit/lclvars.cpp b/src/coreclr/src/jit/lclvars.cpp index 1075b845b50f2c..6d76f7bee939d3 100644 --- a/src/coreclr/src/jit/lclvars.cpp +++ b/src/coreclr/src/jit/lclvars.cpp @@ -355,15 +355,23 @@ void Compiler::lvaInitArgs(InitVarDscInfo* varDscInfo) #if defined(TARGET_WINDOWS) && !defined(TARGET_ARM) if (compMethodIsNativeInstanceMethod(info.compMethodInfo)) { + // If we are a native instance method, handle the first user arg + // (the unmanaged this parameter) and then handle the hidden + // return buffer parameter. assert(numUserArgs >= 1); lvaInitUserArgs(varDscInfo, 0, 1); numUserArgsToSkip++; numUserArgs--; + + lvaInitRetBuffArg(varDscInfo, false); } + else #endif + { + /* If we have a hidden return-buffer parameter, that comes here */ + lvaInitRetBuffArg(varDscInfo, true); + } - /* If we have a hidden return-buffer parameter, that comes here */ - lvaInitRetBuffArg(varDscInfo); //====================================================================== @@ -495,7 +503,7 @@ void Compiler::lvaInitThisPtr(InitVarDscInfo* varDscInfo) } /*****************************************************************************/ -void Compiler::lvaInitRetBuffArg(InitVarDscInfo* varDscInfo) +void Compiler::lvaInitRetBuffArg(InitVarDscInfo* varDscInfo, bool useFixedRetBufReg) { LclVarDsc* varDsc = varDscInfo->varDsc; bool hasRetBuffArg = impMethodInfo_hasRetBuffArg(info.compMethodInfo); @@ -510,7 +518,7 @@ void Compiler::lvaInitRetBuffArg(InitVarDscInfo* varDscInfo) varDsc->lvIsParam = 1; varDsc->lvIsRegArg = 1; - if (hasFixedRetBuffReg()) + if (useFixedRetBufReg && hasFixedRetBuffReg()) { varDsc->SetArgReg(theFixedRetBuffReg()); } From dd44e3213a3ec4dbbf3b78f9ab569907fde506df Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 7 Oct 2020 17:04:09 -0700 Subject: [PATCH 33/73] Update src/coreclr/src/vm/dllimportcallback.cpp Co-authored-by: Jan Kotas --- src/coreclr/src/vm/dllimportcallback.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/src/vm/dllimportcallback.cpp b/src/coreclr/src/vm/dllimportcallback.cpp index 73bad18adf9868..c857736c3c4fc6 100644 --- a/src/coreclr/src/vm/dllimportcallback.cpp +++ b/src/coreclr/src/vm/dllimportcallback.cpp @@ -894,7 +894,7 @@ Stub *UMThunkMarshInfo::CompileNExportThunk(LoaderHeap *pLoaderHeap, PInvokeStat else if (argit.HasValueTypeReturn()) { stubInfo.m_wFlags |= umtmlThisCallHiddenArg | umtmlEnregRetValToBuf; - // When the native signature hasa return buffer but the + // When the native signature has a return buffer but the // managed one does not, we need to handle popping the // the return buffer of the stack manually, which we do here. m_cbRetPop += 4; From 1056c44f6d776e96157d2b91ad8d9e32e0c38eb6 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 7 Oct 2020 21:54:34 -0700 Subject: [PATCH 34/73] Fix Linux x64 crossgen failure. --- src/coreclr/src/jit/compiler.cpp | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/src/coreclr/src/jit/compiler.cpp b/src/coreclr/src/jit/compiler.cpp index 3482d71f8051d4..bbc8b9ffa7344d 100644 --- a/src/coreclr/src/jit/compiler.cpp +++ b/src/coreclr/src/jit/compiler.cpp @@ -951,6 +951,12 @@ var_types Compiler::getReturnTypeForStruct(CORINFO_CLASS_HANDLE clsHnd, { // Return classification is not always size based... canReturnInRegister = structDesc.passedInRegisters; + if (!canReturnInRegister) + { + assert(structDesc.eightByteCount == 0); + howToReturnStruct = SPK_ByReference; + useType = TYP_UNKNOWN; + } } #endif // UNIX_AMD64_ABI @@ -1048,24 +1054,13 @@ var_types Compiler::getReturnTypeForStruct(CORINFO_CLASS_HANDLE clsHnd, #ifdef UNIX_AMD64_ABI - // The case of (structDesc.eightByteCount == 1) should have already been handled - if (structDesc.eightByteCount > 1) - { - // setup wbPassType and useType indicate that this is returned by value in multiple registers - howToReturnStruct = SPK_ByValue; - useType = TYP_STRUCT; - assert(structDesc.passedInRegisters == true); - } - else - { - assert(structDesc.eightByteCount == 0); - // Otherwise we return this struct using a return buffer - // setup wbPassType and useType indicate that this is return using a return buffer register - // (reference to a return buffer) - howToReturnStruct = SPK_ByReference; - useType = TYP_UNKNOWN; - assert(structDesc.passedInRegisters == false); - } + // The cases of (structDesc.eightByteCount == 1) and (structDesc.eightByteCount == 0) + // should have already been handled + assert(structDesc.eightByteCount > 1) + // setup wbPassType and useType indicate that this is returned by value in multiple registers + howToReturnStruct = SPK_ByValue; + useType = TYP_STRUCT; + assert(structDesc.passedInRegisters == true); #elif defined(TARGET_ARM64) From 47bf6aa5a96717ea0391e9e9c7dfb3030aecf429 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 7 Oct 2020 21:55:11 -0700 Subject: [PATCH 35/73] Fix typo. Signed-off-by: Jeremy Koritzinsky --- src/coreclr/src/jit/compiler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/src/jit/compiler.cpp b/src/coreclr/src/jit/compiler.cpp index bbc8b9ffa7344d..dfc76e615e22a3 100644 --- a/src/coreclr/src/jit/compiler.cpp +++ b/src/coreclr/src/jit/compiler.cpp @@ -1056,7 +1056,7 @@ var_types Compiler::getReturnTypeForStruct(CORINFO_CLASS_HANDLE clsHnd, // The cases of (structDesc.eightByteCount == 1) and (structDesc.eightByteCount == 0) // should have already been handled - assert(structDesc.eightByteCount > 1) + assert(structDesc.eightByteCount > 1); // setup wbPassType and useType indicate that this is returned by value in multiple registers howToReturnStruct = SPK_ByValue; useType = TYP_STRUCT; From a1bb21c728031be12970ccdb2bb2567c91f861ff Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 9 Oct 2020 12:39:05 -0700 Subject: [PATCH 36/73] For x86 reverse P/Invokes where the return is an 8-byte struct, handle the case where native expects the returned struct in EAX:EDX but the managed call expects to return the result in a return buffer. Now that interop isn't bashing the return value to long and we aren't changing the x86 managed calling convention to support returning arbitrary 8-byte structs in EAX:EDX, the managed calling convention expects to return an arbitrary 8-byte struct in a return buffer. Since in this case, native code expects the return to be enregistered, we need to introduce a return buffer into the stack while executing the stub so the managed calling convention can point to the stub correctly. --- src/coreclr/src/vm/dllimportcallback.cpp | 78 ++++++++++++++++++++++-- src/coreclr/src/vm/dllimportcallback.h | 1 + 2 files changed, 73 insertions(+), 6 deletions(-) diff --git a/src/coreclr/src/vm/dllimportcallback.cpp b/src/coreclr/src/vm/dllimportcallback.cpp index c857736c3c4fc6..fdc25ec3149135 100644 --- a/src/coreclr/src/vm/dllimportcallback.cpp +++ b/src/coreclr/src/vm/dllimportcallback.cpp @@ -165,7 +165,7 @@ VOID UMEntryThunk::CompileUMThunkWorker(UMThunkStubInfo *pInfo, { // exchange ecx ( "this") with the hidden structure return buffer // xchg ecx, [esp] - pcpusl->X86EmitOp(0x87, kECX, (X86Reg)4 /*ESP*/); + pcpusl->X86EmitOp(0x87, kECX, (X86Reg)kESP_Unsafe); } // jam ecx (the "this" param onto stack. Now it looks like a normal stdcall.) @@ -174,6 +174,28 @@ VOID UMEntryThunk::CompileUMThunkWorker(UMThunkStubInfo *pInfo, // push edx - repush the return address pcpusl->X86EmitPushReg(kEDX); } + + // The native signature doesn't have a return buffer + // but the managed signature does. + // Set up the return buffer address here. + if (pInfo->m_wFlags & umtmlBufRetValToEnreg) + { + // Calculate the return buffer address + // lea edx [esp - ENREGISTERED_RETURNTYPE_MAXSIZE] + pcpusl->X86EmitEspOffset(0x8d, kEDX, -ENREGISTERED_RETURNTYPE_MAXSIZE); + + // exchange edx (which has the return buffer address) + // with the return address + // xchg edx, [esp] + pcpusl->X86EmitOp(0x87, kEDX, (X86Reg)kESP_Unsafe); + + // Make space for the inserted return buffer. + // sub esp ENREGISTERED_RETURNTYPE_MAXSIZE + pcpusl->X86EmitSubEsp(ENREGISTERED_RETURNTYPE_MAXSIZE); + + // push edx + pcpusl->X86EmitPushReg(kEDX); + } // Setup the EBP frame pcpusl->X86EmitPushEBPframe(); @@ -253,6 +275,9 @@ VOID UMEntryThunk::CompileUMThunkWorker(UMThunkStubInfo *pInfo, // push fs:[0] const static BYTE codeSEH1[] = { 0x64, 0xFF, 0x35, 0x0, 0x0, 0x0, 0x0}; pcpusl->EmitBytes(codeSEH1, sizeof(codeSEH1)); + // EmitBytes doesn't know to increase the stack size + // so we do so manually + pcpusl->SetStackSize(pcpusl->GetStackSize() + 4); // link in the exception frame // mov dword ptr fs:[0], esp @@ -470,6 +495,29 @@ VOID UMEntryThunk::CompileUMThunkWorker(UMThunkStubInfo *pInfo, pcpusl->X86EmitIndexRegStore(kEBX, -0x8 /* to outer EBP */ -0x8 /* skip saved EBP, EBX */, kEAX); pcpusl->X86EmitIndexRegStore(kEBX, -0x8 /* to outer EBP */ -0xc /* skip saved EBP, EBX, EAX */, kEDX); } + else if (pInfo->m_wFlags & umtmlBufRetValToEnreg) + { + // We need to copy the return buffer into the outer EDX:EAX register pair. + + // Save EDI and EAX registers so we can use it for scratch. + pcpusl->X86EmitPushReg(kEDI); + pcpusl->X86EmitPushReg(kEAX); + + // mov edi, [ebx + retbufofs] + pcpusl->X86EmitIndexRegLoad(kEDI, kEBX, retbufofs); + // mov eax, [edi] + pcpusl->X86EmitIndexRegLoad(kEAX, kEDI, 0); + // mov [ebx - (offset to outer eax)] eax + pcpusl->X86EmitIndexRegStore(kEBX, -0x8 /* to outer EBP */ -0x8 /* skip saved EBP, EBX */, kEAX); + // mov eax, [edi + 4] + pcpusl->X86EmitIndexRegLoad(kEAX, kEDI, 0x4 /* offset to second slot of return buffer */); + // mov [ebx - (offset to outer edx)] eax + pcpusl->X86EmitIndexRegStore(kEBX, -0x8 /* to outer EBP */ -0xc /* skip saved EBP, EBX, EAX */, kEAX); + + // Restore scratch registers + pcpusl->X86EmitPopReg(kEAX); + pcpusl->X86EmitPopReg(kEDI); + } else { if (pInfo->m_wFlags & umtmlEnregRetValToBuf) @@ -781,6 +829,9 @@ Stub *UMThunkMarshInfo::CompileNExportThunk(LoaderHeap *pLoaderHeap, PInvokeStat if (m_callConv == UINT16_MAX) m_callConv = static_cast(pSigInfo->GetCallConv()); + UMThunkStubInfo stubInfo; + memset(&stubInfo, 0, sizeof(stubInfo)); + // process this if (!fIsStatic) { @@ -791,16 +842,27 @@ Stub *UMThunkMarshInfo::CompileNExportThunk(LoaderHeap *pLoaderHeap, PInvokeStat // process the return buffer parameter if (argit.HasRetBuffArg() || (m_callConv == pmCallConvThiscall && argit.HasValueTypeReturn())) { - // Only copy the retbuf arg from the src call when the managed call will also + // Only copy the retbuf arg from the src call when both the managed call and native call // have a return buffer. if (argit.HasRetBuffArg()) { + // managed has a return buffer + if (m_callConv != pmCallConvThiscall && + argit.HasValueTypeReturn() && + pMetaSig->GetReturnTypeSize() == ENREGISTERED_RETURNTYPE_MAXSIZE) + { + // Only managed has a return buffer. + // Native returns in registers. + // We add a flag so the stub correctly sets up the return buffer. + stubInfo.m_wFlags |= umtmlBufRetValToEnreg; + // Reserve space for the return buffer. + nOffset += ENREGISTERED_RETURNTYPE_MAXSIZE; + } numRegistersUsed++; _ASSERTE(numRegistersUsed - 1 < NUM_ARGUMENT_REGISTERS); psrcofsregs[NUM_ARGUMENT_REGISTERS - numRegistersUsed] = nOffset; } retbufofs = nOffset; - nOffset += StackElemSize(sizeof(LPVOID)); } @@ -865,9 +927,6 @@ Stub *UMThunkMarshInfo::CompileNExportThunk(LoaderHeap *pLoaderHeap, PInvokeStat m_cbActualArgSize = cbActualArgSize; - UMThunkStubInfo stubInfo; - memset(&stubInfo, 0, sizeof(stubInfo)); - if (!FitsInU2(m_cbActualArgSize)) COMPlusThrow(kMarshalDirectiveException, IDS_EE_SIGTOOCOMPLEX); @@ -901,6 +960,13 @@ Stub *UMThunkMarshInfo::CompileNExportThunk(LoaderHeap *pLoaderHeap, PInvokeStat } } } + + if (stubInfo.m_wFlags & umtmlBufRetValToEnreg) + { + // Pop off the return buffer we insert into the stub. + m_cbRetPop += ENREGISTERED_RETURNTYPE_MAXSIZE; + } + stubInfo.m_cbRetPop = m_cbRetPop; if (fIsStatic) stubInfo.m_wFlags |= umtmlIsStatic; diff --git a/src/coreclr/src/vm/dllimportcallback.h b/src/coreclr/src/vm/dllimportcallback.h index 5c6124a53e1c62..d2360944a88fcc 100644 --- a/src/coreclr/src/vm/dllimportcallback.h +++ b/src/coreclr/src/vm/dllimportcallback.h @@ -23,6 +23,7 @@ enum UMThunkStubFlags umtmlThisCallHiddenArg = 0x0004, umtmlFpu = 0x0008, umtmlEnregRetValToBuf = 0x0010, + umtmlBufRetValToEnreg = 0x0020, #ifdef TARGET_X86 // the signature is trivial so stub need not be generated and the target can be called directly umtmlSkipStub = 0x0080, From 254063b37c4dca171336748afbe25f3e2e7e5d13 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 9 Oct 2020 13:15:14 -0700 Subject: [PATCH 37/73] Flip EAX/EDX slots in the x86 reverse P/Invoke stub. Now that they match the order that a return buffer uses, we can remove creation of our custom return buffer and instead point the return buffer directly into the EAX/EDX slots so our EAX/EDX restore path automatically handles the "native enregistered return, managed retrun buffer return" case. --- src/coreclr/src/vm/dllimportcallback.cpp | 62 ++++++------------------ 1 file changed, 16 insertions(+), 46 deletions(-) diff --git a/src/coreclr/src/vm/dllimportcallback.cpp b/src/coreclr/src/vm/dllimportcallback.cpp index fdc25ec3149135..1e13e96dcc8d21 100644 --- a/src/coreclr/src/vm/dllimportcallback.cpp +++ b/src/coreclr/src/vm/dllimportcallback.cpp @@ -181,18 +181,15 @@ VOID UMEntryThunk::CompileUMThunkWorker(UMThunkStubInfo *pInfo, if (pInfo->m_wFlags & umtmlBufRetValToEnreg) { // Calculate the return buffer address - // lea edx [esp - ENREGISTERED_RETURNTYPE_MAXSIZE] - pcpusl->X86EmitEspOffset(0x8d, kEDX, -ENREGISTERED_RETURNTYPE_MAXSIZE); + // Calculate the offset to the return buffer we establish for EAX:EDX below. + // lea edx [esp - offset to EAX:EDX return buffer] + pcpusl->X86EmitEspOffset(0x8d, kEDX, -0xc /* skip return addr, EBP, EBX */ -0x8 /* point to start of EAX:EDX return buffer */ ); // exchange edx (which has the return buffer address) // with the return address // xchg edx, [esp] - pcpusl->X86EmitOp(0x87, kEDX, (X86Reg)kESP_Unsafe); - - // Make space for the inserted return buffer. - // sub esp ENREGISTERED_RETURNTYPE_MAXSIZE - pcpusl->X86EmitSubEsp(ENREGISTERED_RETURNTYPE_MAXSIZE); - + pcpusl->X86EmitOp(0x87, kEDX, (X86Reg)kESP_Unsafe); + // push edx pcpusl->X86EmitPushReg(kEDX); } @@ -313,9 +310,9 @@ VOID UMEntryThunk::CompileUMThunkWorker(UMThunkStubInfo *pInfo, // // | | // +-------------------------+ - // EBX - 20 | Saved Result: EDX/ST(0) | + // EBX - 20 | Saved Result: EAX/ST(0) | // +- - - - - - - - - - - - -+ - // EBX - 16 | Saved Result: EAX/ST(0) | + // EBX - 16 | Saved Result: EDX/ST(0) | // +-------------------------+ // EBX - 12 | Caller's EBX | // +-------------------------+ @@ -492,33 +489,14 @@ VOID UMEntryThunk::CompileUMThunkWorker(UMThunkStubInfo *pInfo, // save EDX:EAX if (retbufofs == UNUSED_STACK_OFFSET) { - pcpusl->X86EmitIndexRegStore(kEBX, -0x8 /* to outer EBP */ -0x8 /* skip saved EBP, EBX */, kEAX); - pcpusl->X86EmitIndexRegStore(kEBX, -0x8 /* to outer EBP */ -0xc /* skip saved EBP, EBX, EAX */, kEDX); + pcpusl->X86EmitIndexRegStore(kEBX, -0x8 /* to outer EBP */ -0xc /* skip saved EBP, EBX, EDX */, kEAX); + pcpusl->X86EmitIndexRegStore(kEBX, -0x8 /* to outer EBP */ -0x8 /* skip saved EBP, EBX */, kEDX); } - else if (pInfo->m_wFlags & umtmlBufRetValToEnreg) - { - // We need to copy the return buffer into the outer EDX:EAX register pair. - - // Save EDI and EAX registers so we can use it for scratch. - pcpusl->X86EmitPushReg(kEDI); - pcpusl->X86EmitPushReg(kEAX); - - // mov edi, [ebx + retbufofs] - pcpusl->X86EmitIndexRegLoad(kEDI, kEBX, retbufofs); - // mov eax, [edi] - pcpusl->X86EmitIndexRegLoad(kEAX, kEDI, 0); - // mov [ebx - (offset to outer eax)] eax - pcpusl->X86EmitIndexRegStore(kEBX, -0x8 /* to outer EBP */ -0x8 /* skip saved EBP, EBX */, kEAX); - // mov eax, [edi + 4] - pcpusl->X86EmitIndexRegLoad(kEAX, kEDI, 0x4 /* offset to second slot of return buffer */); - // mov [ebx - (offset to outer edx)] eax - pcpusl->X86EmitIndexRegStore(kEBX, -0x8 /* to outer EBP */ -0xc /* skip saved EBP, EBX, EAX */, kEAX); - - // Restore scratch registers - pcpusl->X86EmitPopReg(kEAX); - pcpusl->X86EmitPopReg(kEDI); - } - else + // In the umtmlBufRetValToEnreg case, + // we set up the return buffer to output + // into the EDX:EAX buffer we set up for the register return case. + // So we don't need to do more work here. + else if ((pInfo->m_wFlags & umtmlBufRetValToEnreg) == 0) { if (pInfo->m_wFlags & umtmlEnregRetValToBuf) { @@ -538,7 +516,7 @@ VOID UMEntryThunk::CompileUMThunkWorker(UMThunkStubInfo *pInfo, pcpusl->X86EmitIndexRegLoad(kEAX, kEBX, retbufofs); // save it as the return value - pcpusl->X86EmitIndexRegStore(kEBX, -0x8 /* to outer EBP */ -0x8 /* skip saved EBP, EBX */, kEAX); + pcpusl->X86EmitIndexRegStore(kEBX, -0x8 /* to outer EBP */ -0xc /* skip saved EBP, EBX, EDX */, kEAX); } } @@ -610,8 +588,8 @@ VOID UMEntryThunk::CompileUMThunkWorker(UMThunkStubInfo *pInfo, } else { - pcpusl->X86EmitPopReg(kEDX); pcpusl->X86EmitPopReg(kEAX); + pcpusl->X86EmitPopReg(kEDX); } // Restore EBX, which was saved in prolog @@ -855,8 +833,6 @@ Stub *UMThunkMarshInfo::CompileNExportThunk(LoaderHeap *pLoaderHeap, PInvokeStat // Native returns in registers. // We add a flag so the stub correctly sets up the return buffer. stubInfo.m_wFlags |= umtmlBufRetValToEnreg; - // Reserve space for the return buffer. - nOffset += ENREGISTERED_RETURNTYPE_MAXSIZE; } numRegistersUsed++; _ASSERTE(numRegistersUsed - 1 < NUM_ARGUMENT_REGISTERS); @@ -961,12 +937,6 @@ Stub *UMThunkMarshInfo::CompileNExportThunk(LoaderHeap *pLoaderHeap, PInvokeStat } } - if (stubInfo.m_wFlags & umtmlBufRetValToEnreg) - { - // Pop off the return buffer we insert into the stub. - m_cbRetPop += ENREGISTERED_RETURNTYPE_MAXSIZE; - } - stubInfo.m_cbRetPop = m_cbRetPop; if (fIsStatic) stubInfo.m_wFlags |= umtmlIsStatic; From 71adacb25e512dc963b7b75a906510247169bc4c Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 12 Oct 2020 10:25:07 -0700 Subject: [PATCH 38/73] Apply format patch. --- src/coreclr/src/jit/compiler.h | 4 ++-- src/coreclr/src/jit/importer.cpp | 2 +- src/coreclr/src/jit/lclvars.cpp | 1 - src/coreclr/src/jit/morph.cpp | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/coreclr/src/jit/compiler.h b/src/coreclr/src/jit/compiler.h index f6a3cfb2f1f383..5ffe4c59e8bd7a 100644 --- a/src/coreclr/src/jit/compiler.h +++ b/src/coreclr/src/jit/compiler.h @@ -9321,8 +9321,8 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #ifdef TARGET_AMD64 return (info.compRetBuffArg != BAD_VAR_NUM); #elif defined(TARGET_WINDOWS) && defined(TARGET_ARM64) - return (compMethodIsNativeInstanceMethod(info.compMethodInfo) || - compIsProfilerHookNeeded()) && (info.compRetBuffArg != BAD_VAR_NUM); + return (compMethodIsNativeInstanceMethod(info.compMethodInfo) || compIsProfilerHookNeeded()) && + (info.compRetBuffArg != BAD_VAR_NUM); #else // !TARGET_AMD64 return (compIsProfilerHookNeeded()) && (info.compRetBuffArg != BAD_VAR_NUM); #endif // !TARGET_AMD64 diff --git a/src/coreclr/src/jit/importer.cpp b/src/coreclr/src/jit/importer.cpp index e4ef3b5ee5df66..916ab918f26bae 100644 --- a/src/coreclr/src/jit/importer.cpp +++ b/src/coreclr/src/jit/importer.cpp @@ -17153,7 +17153,7 @@ bool Compiler::impReturnInstruction(int prefixFlags, OPCODE& opcode) // return the implicit return buffer explicitly (in RAX). // Change the return type to be BYREF. op1 = gtNewOperNode(GT_RETURN, TYP_BYREF, gtNewLclvNode(info.compRetBuffArg, TYP_BYREF)); -#else // !defined(TARGET_AMD64) +#else // !defined(TARGET_AMD64) // In case of non-AMD64 targets the profiler hook requires to return the implicit RetBuf explicitly (in RAX). // In such case the return value of the function is changed to BYREF. // If profiler hook is not needed the return type of the function is TYP_VOID. diff --git a/src/coreclr/src/jit/lclvars.cpp b/src/coreclr/src/jit/lclvars.cpp index 6d76f7bee939d3..3024d7ab056b16 100644 --- a/src/coreclr/src/jit/lclvars.cpp +++ b/src/coreclr/src/jit/lclvars.cpp @@ -372,7 +372,6 @@ void Compiler::lvaInitArgs(InitVarDscInfo* varDscInfo) lvaInitRetBuffArg(varDscInfo, true); } - //====================================================================== #if USER_ARGS_COME_LAST diff --git a/src/coreclr/src/jit/morph.cpp b/src/coreclr/src/jit/morph.cpp index 853635b48277ac..418613f8b97352 100644 --- a/src/coreclr/src/jit/morph.cpp +++ b/src/coreclr/src/jit/morph.cpp @@ -2664,7 +2664,7 @@ void Compiler::fgInitArgInfo(GenTreeCall* call) assert(arg2 != nullptr); nonStandardArgs.Add(arg2, REG_LNGARG_HI); } -#else // !TARGET_X86 +#else // !TARGET_X86 // TODO-X86-CQ: Currently RyuJIT/x86 passes args on the stack, so this is not needed. // If/when we change that, the following code needs to be changed to correctly support the (TBD) managed calling // convention for x86/SSE. From 195713b9f06c79f54efee80ea0676a044051bd68 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 12 Oct 2020 11:30:30 -0700 Subject: [PATCH 39/73] Disable test on Win ARM32 to avoid blocking CI. Not fixing the test since we don't officially support Windows ARM32 anyway and this scenario is already a corner case. Signed-off-by: Jeremy Koritzinsky --- src/tests/issues.targets | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tests/issues.targets b/src/tests/issues.targets index 9458cb45c44ef0..f69133b7937467 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -399,6 +399,9 @@ https://github.com/dotnet/runtime/issues/12299 + + Thiscall not supported on Windows ARM32. + https://github.com/dotnet/runtime/issues/12979 From 8833b06ef362f5417110230a4c8c99ddd91899e8 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 14 Oct 2020 11:13:31 -0700 Subject: [PATCH 40/73] Restore pointer-sized trivial struct normalization, this time on-demand in the JIT. --- src/coreclr/src/jit/compiler.cpp | 54 ++++++++++++++++++++++++++++++-- src/coreclr/src/jit/compiler.h | 4 +++ src/coreclr/src/jit/lclvars.cpp | 6 ++++ src/coreclr/src/jit/morph.cpp | 5 ++- 4 files changed, 64 insertions(+), 5 deletions(-) diff --git a/src/coreclr/src/jit/compiler.cpp b/src/coreclr/src/jit/compiler.cpp index dfc76e615e22a3..fcf6f0b6b4f839 100644 --- a/src/coreclr/src/jit/compiler.cpp +++ b/src/coreclr/src/jit/compiler.cpp @@ -512,6 +512,52 @@ bool Compiler::isSingleFloat32Struct(CORINFO_CLASS_HANDLE clsHnd) } #endif // ARM_SOFTFP +#ifdef TARGET_X86 +//--------------------------------------------------------------------------- +// isTrivialPointerSizedStruct: +// Check if the given struct type contains only one pointer-sized integer value type +// +// Arguments: +// clsHnd - the handle for the struct type +// +// Return Value: +// true if the given struct type contains only one pointer-sized integer value type, +// false otherwise. +// + +bool Compiler::isTrivialPointerSizedStruct(CORINFO_CLASS_HANDLE clsHnd) +{ + assert(info.compCompHnd->isValueClass(clsHnd)); + for (;;) + { + // all of class chain must be of value type and must have only one field + if (!info.compCompHnd->isValueClass(clsHnd) || info.compCompHnd->getClassNumInstanceFields(clsHnd) != 1) + { + return false; + } + + CORINFO_CLASS_HANDLE* pClsHnd = &clsHnd; + CORINFO_FIELD_HANDLE fldHnd = info.compCompHnd->getFieldInClass(clsHnd, 0); + CorInfoType fieldType = info.compCompHnd->getFieldType(fldHnd, pClsHnd); + + var_types vt = JITtype2varType(fieldType); + + if (fieldType == CORINFO_TYPE_VALUECLASS) + { + clsHnd = *pClsHnd; + } + else if (varTypeIsI(vt) && !varTypeIsGC(vt)) + { + return true; + } + else + { + return false; + } + } +} +#endif // TARGET_X86 + //----------------------------------------------------------------------------- // getPrimitiveTypeForStruct: // Get the "primitive" type that is is used for a struct @@ -693,7 +739,7 @@ var_types Compiler::getArgTypeForStruct(CORINFO_CLASS_HANDLE clsHnd, assert(structSize != 0); // Determine if we can pass the struct as a primitive type. -// Note that on x86 we never pass structs as primitive types (unless the VM unwraps them for us). +// Note that on x86 we only pass specific pointer-sized structs as primitives that the VM used to unwrap. #ifndef TARGET_X86 #ifdef UNIX_AMD64_ABI @@ -728,7 +774,11 @@ var_types Compiler::getArgTypeForStruct(CORINFO_CLASS_HANDLE clsHnd, // and also examine the clsHnd to see if it is an HFA of count one useType = getPrimitiveTypeForStruct(structSize, clsHnd, isVarArg); } - +#else + if (isTrivialPointerSizedStruct(clsHnd)) + { + useType = TYP_I_IMPL; + } #endif // !TARGET_X86 // Did we change this struct type into a simple "primitive" type? diff --git a/src/coreclr/src/jit/compiler.h b/src/coreclr/src/jit/compiler.h index 5ffe4c59e8bd7a..7d0ebf08f731b3 100644 --- a/src/coreclr/src/jit/compiler.h +++ b/src/coreclr/src/jit/compiler.h @@ -2254,6 +2254,10 @@ class Compiler bool isSingleFloat32Struct(CORINFO_CLASS_HANDLE hClass); #endif // ARM_SOFTFP +#ifdef TARGET_X86 + bool isTrivialPointerSizedStruct(CORINFO_CLASS_HANDLE clsHnd); +#endif // TARGET_X86 + //------------------------------------------------------------------------- // Functions to handle homogeneous floating-point aggregates (HFAs) in ARM/ARM64. // HFAs are one to four element structs where each element is the same diff --git a/src/coreclr/src/jit/lclvars.cpp b/src/coreclr/src/jit/lclvars.cpp index 3024d7ab056b16..99bf95de23d81b 100644 --- a/src/coreclr/src/jit/lclvars.cpp +++ b/src/coreclr/src/jit/lclvars.cpp @@ -837,6 +837,12 @@ void Compiler::lvaInitUserArgs(InitVarDscInfo* varDscInfo, unsigned skipArgs, un canPassArgInRegisters = structDesc.passedInRegisters; } else +#elif defined(TARGET_X86) + if (varTypeIsStruct(argType) && isTrivialPointerSizedStruct(typeHnd)) + { + canPassArgInRegisters = varDscInfo->canEnreg(TYP_I_IMPL, cSlotsToEnregister); + } + else #endif // defined(UNIX_AMD64_ABI) { canPassArgInRegisters = varDscInfo->canEnreg(argType, cSlotsToEnregister); diff --git a/src/coreclr/src/jit/morph.cpp b/src/coreclr/src/jit/morph.cpp index 418613f8b97352..63c93dd5beb8ff 100644 --- a/src/coreclr/src/jit/morph.cpp +++ b/src/coreclr/src/jit/morph.cpp @@ -3178,6 +3178,8 @@ void Compiler::fgInitArgInfo(GenTreeCall* call) if (isRegParamType(genActualType(argx->TypeGet())) #ifdef UNIX_AMD64_ABI && (!isStructArg || structDesc.passedInRegisters) +#elif defined(TARGET_X86) + || (isStructArg && isTrivialPointerSizedStruct(objClass)) #endif ) { @@ -3706,8 +3708,6 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call) } else // This is passed by value. { - -#ifndef TARGET_X86 // Check to see if we can transform this into load of a primitive type. // 'size' must be the number of pointer sized items DEBUG_ARG_SLOTS_ASSERT(size == roundupSize / TARGET_POINTER_SIZE); @@ -3899,7 +3899,6 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call) assert(varTypeIsEnregisterable(argObj->TypeGet()) || ((copyBlkClass != NO_CLASS_HANDLE) && varTypeIsEnregisterable(structBaseType))); } -#endif // !TARGET_X86 #ifndef UNIX_AMD64_ABI // We still have a struct unless we converted the GT_OBJ into a GT_IND above... From 7e2c1f9f20fe5dda327404d2208124abcea86974 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 14 Oct 2020 11:52:46 -0700 Subject: [PATCH 41/73] Ensure we normalize in ArgIterator. --- src/coreclr/src/vm/callingconvention.h | 46 +++++++++++++++++++++--- src/coreclr/src/vm/comtoclrcall.cpp | 2 +- src/coreclr/src/vm/dllimportcallback.cpp | 6 ++-- 3 files changed, 45 insertions(+), 9 deletions(-) diff --git a/src/coreclr/src/vm/callingconvention.h b/src/coreclr/src/vm/callingconvention.h index a7b070d153b783..a86bc5306ff8ad 100644 --- a/src/coreclr/src/vm/callingconvention.h +++ b/src/coreclr/src/vm/callingconvention.h @@ -387,11 +387,47 @@ class ArgIteratorTemplate : public ARGITERATOR_BASE // // typ: the signature type //========================================================================= - static BOOL IsArgumentInRegister(int * pNumRegistersUsed, CorElementType typ) + static BOOL IsArgumentInRegister(int * pNumRegistersUsed, CorElementType typ, TypeHandle hnd) { LIMITED_METHOD_CONTRACT; - if ( (*pNumRegistersUsed) < NUM_ARGUMENT_REGISTERS) { - if (gElementTypeInfo[typ].m_enregister) { + if ( (*pNumRegistersUsed) < NUM_ARGUMENT_REGISTERS) + { + if (typ == ELEMENT_TYPE_VALUETYPE) + { + // The JIT enables passing trivial pointer sized structs in registers. + MethodTable* pMT = hnd.GetMethodTable(); + + while (typ == ELEMENT_TYPE_VALUETYPE && + pMT->GetNumInstanceFields() == 1 && (!pMT->HasLayout() || + pMT->GetNumInstanceFieldBytes() == 4 + )) // Don't do the optimization if we're getting specified anything but the trivial layout. + { + FieldDesc * pFD = pMT->GetApproxFieldDescListRaw(); + CorElementType type = pFD->GetFieldType(); + + switch (type) + { + case ELEMENT_TYPE_VALUETYPE: + { + //@todo: Is it more apropos to call LookupApproxFieldTypeHandle() here? + TypeHandle fldHnd = pFD->GetApproxFieldTypeHandleThrowing(); + CONSISTENCY_CHECK(!fldHnd.IsNull()); + pMT = fldHnd->GetMethodTable(); + } + case ELEMENT_TYPE_PTR: + case ELEMENT_TYPE_I: + case ELEMENT_TYPE_U: + case ELEMENT_TYPE_I4: + case ELEMENT_TYPE_U4: + { + typ = type; + break; + } + } + } + } + if (gElementTypeInfo[typ].m_enregister) + { (*pNumRegistersUsed)++; return(TRUE); } @@ -1050,7 +1086,7 @@ int ArgIteratorTemplate::GetNextOffset() return argOfs; } #endif - if (IsArgumentInRegister(&m_numRegistersUsed, argType)) + if (IsArgumentInRegister(&m_numRegistersUsed, argType, thValueType)) { return TransitionBlock::GetOffsetOfArgumentRegisters() + (NUM_ARGUMENT_REGISTERS - m_numRegistersUsed) * sizeof(void *); } @@ -1627,7 +1663,7 @@ void ArgIteratorTemplate::ForceSigWalk() TypeHandle thValueType; CorElementType type = this->GetNextArgumentType(i, &thValueType); - if (!IsArgumentInRegister(&numRegistersUsed, type)) + if (!IsArgumentInRegister(&numRegistersUsed, type, thValueType)) { int structSize = MetaSig::GetElemSize(type, thValueType); diff --git a/src/coreclr/src/vm/comtoclrcall.cpp b/src/coreclr/src/vm/comtoclrcall.cpp index 7dd54ea93e1c6b..6a68c2c2009d1d 100644 --- a/src/coreclr/src/vm/comtoclrcall.cpp +++ b/src/coreclr/src/vm/comtoclrcall.cpp @@ -862,7 +862,7 @@ void ComCallMethodDesc::InitRuntimeNativeInfo(MethodDesc *pStubMD) UINT cbSize = MetaSig::GetElemSize(type, thValueType); - if (ArgIterator::IsArgumentInRegister(&numRegistersUsed, type)) + if (ArgIterator::IsArgumentInRegister(&numRegistersUsed, type, thValueType)) { wSourceSlotEDX = wInputStack / STACK_ELEM_SIZE; wInputStack += STACK_ELEM_SIZE; diff --git a/src/coreclr/src/vm/dllimportcallback.cpp b/src/coreclr/src/vm/dllimportcallback.cpp index 1e13e96dcc8d21..bd4945716f35dc 100644 --- a/src/coreclr/src/vm/dllimportcallback.cpp +++ b/src/coreclr/src/vm/dllimportcallback.cpp @@ -864,7 +864,7 @@ Stub *UMThunkMarshInfo::CompileNExportThunk(LoaderHeap *pLoaderHeap, PInvokeStat fPassPointer = TRUE; } - if (ArgIterator::IsArgumentInRegister(&numRegistersUsed, type)) + if (ArgIterator::IsArgumentInRegister(&numRegistersUsed, type, thValueType)) { _ASSERTE(numRegistersUsed - 1 < NUM_ARGUMENT_REGISTERS); psrcofsregs[NUM_ARGUMENT_REGISTERS - numRegistersUsed] = @@ -1448,7 +1448,7 @@ VOID UMThunkMarshInfo::RunTimeInit() TypeHandle thValueType; CorElementType type = sig.NextArgNormalized(&thValueType); int cbSize = sig.GetElemSize(type, thValueType); - if (ArgIterator::IsArgumentInRegister(&numRegistersUsed, type)) + if (ArgIterator::IsArgumentInRegister(&numRegistersUsed, type, thValueType)) { offs += STACK_ELEM_SIZE; } @@ -1533,7 +1533,7 @@ VOID UMThunkMarshInfo::SetupArguments(char *pSrc, ArgumentRegisters *pArgRegs, c int cbSize = sig.GetElemSize(type, thValueType); int elemSize = StackElemSize(cbSize); - if (ArgIterator::IsArgumentInRegister(&numRegistersUsed, type)) + if (ArgIterator::IsArgumentInRegister(&numRegistersUsed, type, thValueType)) { _ASSERTE(elemSize == STACK_ELEM_SIZE); From 382113f73a2da49f4b79f9d9cfd1a62647c94a90 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 14 Oct 2020 11:55:24 -0700 Subject: [PATCH 42/73] Explicitly validate size for this optimization. Signed-off-by: Jeremy Koritzinsky --- src/coreclr/src/jit/compiler.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/coreclr/src/jit/compiler.cpp b/src/coreclr/src/jit/compiler.cpp index fcf6f0b6b4f839..11d44fd6712d36 100644 --- a/src/coreclr/src/jit/compiler.cpp +++ b/src/coreclr/src/jit/compiler.cpp @@ -528,6 +528,10 @@ bool Compiler::isSingleFloat32Struct(CORINFO_CLASS_HANDLE clsHnd) bool Compiler::isTrivialPointerSizedStruct(CORINFO_CLASS_HANDLE clsHnd) { assert(info.compCompHnd->isValueClass(clsHnd)); + if (info.compCompHnd->getClassSize(clsHnd) != TARGET_POINTER_SIZE) + { + return false; + } for (;;) { // all of class chain must be of value type and must have only one field From bd4022a7f1a46e749e54c874e26d1374c24063bc Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 14 Oct 2020 11:58:48 -0700 Subject: [PATCH 43/73] Fix typo Signed-off-by: Jeremy Koritzinsky --- src/coreclr/src/vm/callingconvention.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/src/vm/callingconvention.h b/src/coreclr/src/vm/callingconvention.h index a86bc5306ff8ad..68331777c16787 100644 --- a/src/coreclr/src/vm/callingconvention.h +++ b/src/coreclr/src/vm/callingconvention.h @@ -412,7 +412,7 @@ class ArgIteratorTemplate : public ARGITERATOR_BASE //@todo: Is it more apropos to call LookupApproxFieldTypeHandle() here? TypeHandle fldHnd = pFD->GetApproxFieldTypeHandleThrowing(); CONSISTENCY_CHECK(!fldHnd.IsNull()); - pMT = fldHnd->GetMethodTable(); + pMT = fldHnd.GetMethodTable(); } case ELEMENT_TYPE_PTR: case ELEMENT_TYPE_I: From 1694da82feb8da682c7bc7c0f4b70c82405bb804 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 15 Oct 2020 15:28:23 -0700 Subject: [PATCH 44/73] Fix infinite loop in negative case in VM impl of normalization algorithm --- src/coreclr/src/vm/callingconvention.h | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/coreclr/src/vm/callingconvention.h b/src/coreclr/src/vm/callingconvention.h index 68331777c16787..129970032a0a4e 100644 --- a/src/coreclr/src/vm/callingconvention.h +++ b/src/coreclr/src/vm/callingconvention.h @@ -405,6 +405,7 @@ class ArgIteratorTemplate : public ARGITERATOR_BASE FieldDesc * pFD = pMT->GetApproxFieldDescListRaw(); CorElementType type = pFD->GetFieldType(); + bool exitLoop = false; switch (type) { case ELEMENT_TYPE_VALUETYPE: @@ -422,8 +423,16 @@ class ArgIteratorTemplate : public ARGITERATOR_BASE { typ = type; break; - } - } + } + default: + exitLoop = true; + break; + } + + if (exitLoop) + { + break; + } } } if (gElementTypeInfo[typ].m_enregister) From 5dd9031e7d3648ad3ce2ddb4aabac09a107156ba Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 16 Oct 2020 10:41:23 -0700 Subject: [PATCH 45/73] Fix promoting fields of structs that are passed in register into a call on x86. --- src/coreclr/src/jit/lclvars.cpp | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/coreclr/src/jit/lclvars.cpp b/src/coreclr/src/jit/lclvars.cpp index 83798ae0bb2dcb..af890ed6b57df1 100644 --- a/src/coreclr/src/jit/lclvars.cpp +++ b/src/coreclr/src/jit/lclvars.cpp @@ -2317,7 +2317,7 @@ void Compiler::StructPromotionHelper::PromoteStructVar(unsigned lclNum) #endif -#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_ARM) +#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_ARM) || defined(TARGET_X86) // Do we have a parameter that can be enregistered? // @@ -2357,7 +2357,7 @@ void Compiler::StructPromotionHelper::PromoteStructVar(unsigned lclNum) fieldVarDsc->SetArgReg(parentArgReg); } } -#endif // defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_ARM) +#endif // defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_ARM) || defined(TARGET_X86) #ifdef FEATURE_SIMD if (varTypeIsSIMD(pFieldInfo->fldType)) @@ -5129,12 +5129,18 @@ void Compiler::lvaFixVirtualFrameOffsets() // Is this a non-param promoted struct field? // if so then set doAssignStkOffs to false. // - if (varDsc->lvIsStructField && !varDsc->lvIsParam) + if (varDsc->lvIsStructField) { LclVarDsc* parentvarDsc = &lvaTable[varDsc->lvParentLcl]; lvaPromotionType promotionType = lvaGetPromotionType(parentvarDsc); - if (promotionType == PROMOTION_TYPE_DEPENDENT) +#if defined(TARGET_X86) + // On x86, we set the stack offset for a promoted field + // to match a struct parameter in lvAssignFrameOffsetsToPromotedStructs. + if ((!varDsc->lvIsParam || parentvarDsc->lvIsParam) && promotionType == PROMOTION_TYPE_DEPENDENT) +#else + if (!varDsc->lvIsParam && promotionType == PROMOTION_TYPE_DEPENDENT) +#endif { doAssignStkOffs = false; // Assigned later in lvaAssignFrameOffsetsToPromotedStructs() } @@ -6944,17 +6950,16 @@ void Compiler::lvaAssignFrameOffsetsToPromotedStructs() // outgoing args space. Assign the dependently promoted fields properly. // if (varDsc->lvIsStructField -#ifndef UNIX_AMD64_ABI -#if !defined(TARGET_ARM) +#if !defined(UNIX_AMD64_ABI) && !defined(TARGET_ARM) && !defined(TARGET_X86) // ARM: lo/hi parts of a promoted long arg need to be updated. // For System V platforms there is no outgoing args space. - // A register passed struct arg is homed on the stack in a separate local var. + + // For System V and x86, a register passed struct arg is homed on the stack in a separate local var. // The offset of these structs is already calculated in lvaAssignVirtualFrameOffsetToArg methos. // Make sure the code below is not executed for these structs and the offset is not changed. && !varDsc->lvIsParam -#endif // !defined(TARGET_ARM) -#endif // !UNIX_AMD64_ABI +#endif // !defined(UNIX_AMD64_ABI) && !defined(TARGET_ARM) && !defined(TARGET_X86) ) { LclVarDsc* parentvarDsc = &lvaTable[varDsc->lvParentLcl]; From 7182ad05c09e98db5d6dac70bd3a5747650e21bd Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 16 Oct 2020 14:49:31 -0700 Subject: [PATCH 46/73] Fixed passing a trivial struct in registers to a fast tail call (that generates a direct jmp instruction). --- src/coreclr/src/jit/codegenxarch.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/coreclr/src/jit/codegenxarch.cpp b/src/coreclr/src/jit/codegenxarch.cpp index abb0743b0b8600..660d68dd60ebb6 100644 --- a/src/coreclr/src/jit/codegenxarch.cpp +++ b/src/coreclr/src/jit/codegenxarch.cpp @@ -5711,11 +5711,26 @@ void CodeGen::genJmpMethod(GenTree* jmp) #endif // !defined(UNIX_AMD64_ABI) { // Register argument +#ifdef TARGET_X86 + noway_assert(isRegParamType(genActualType(varDsc->TypeGet())) || + (varTypeIsStruct(varDsc->TypeGet()) && compiler->isTrivialPointerSizedStruct(varDsc->GetStructHnd()))); +#else noway_assert(isRegParamType(genActualType(varDsc->TypeGet()))); +#endif // TARGET_X86 // Is register argument already in the right register? // If not load it from its stack location. var_types loadType = varDsc->lvaArgType(); + +#ifdef TARGET_X86 + if (varTypeIsStruct(varDsc->TypeGet()) && compiler->isTrivialPointerSizedStruct(varDsc->GetStructHnd())) + { + // Treat trivial pointer-sized structs as a pointer sized primitive + // for the purposes of registers. + loadType = TYP_I_IMPL; + } +#endif + regNumber argReg = varDsc->GetArgReg(); // incoming arg register if (varDsc->GetRegNum() != argReg) From e329242019c629580c388037a1edc80aa99c2071 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 19 Oct 2020 10:46:36 -0700 Subject: [PATCH 47/73] Fix formatting --- src/coreclr/src/jit/codegenxarch.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/coreclr/src/jit/codegenxarch.cpp b/src/coreclr/src/jit/codegenxarch.cpp index 660d68dd60ebb6..c2056981e6cb25 100644 --- a/src/coreclr/src/jit/codegenxarch.cpp +++ b/src/coreclr/src/jit/codegenxarch.cpp @@ -5711,8 +5711,10 @@ void CodeGen::genJmpMethod(GenTree* jmp) #endif // !defined(UNIX_AMD64_ABI) { // Register argument + CLANG_FORMAT_COMMENT_ANCHOR; #ifdef TARGET_X86 - noway_assert(isRegParamType(genActualType(varDsc->TypeGet())) || + noway_assert( + isRegParamType(genActualType(varDsc->TypeGet())) || (varTypeIsStruct(varDsc->TypeGet()) && compiler->isTrivialPointerSizedStruct(varDsc->GetStructHnd()))); #else noway_assert(isRegParamType(genActualType(varDsc->TypeGet()))); @@ -5731,7 +5733,7 @@ void CodeGen::genJmpMethod(GenTree* jmp) } #endif - regNumber argReg = varDsc->GetArgReg(); // incoming arg register + regNumber argReg = varDsc->GetArgReg(); // incoming arg register if (varDsc->GetRegNum() != argReg) { From 94f6a1d9094e4bf39fa5ac4667d70cdaa009c1aa Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 19 Oct 2020 12:38:04 -0700 Subject: [PATCH 48/73] Update crossgen2 to the "JIT does the struct normalization" plan. --- .../tools/Common/JitInterface/CorInfoImpl.cs | 15 ----- .../ReadyToRun/ArgIterator.cs | 12 +--- .../ReadyToRun/TransitionBlock.cs | 48 ++++++++++++++ .../Compiler/ReadyToRunCompilerContext.cs | 64 ------------------- 4 files changed, 49 insertions(+), 90 deletions(-) diff --git a/src/coreclr/src/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/src/tools/Common/JitInterface/CorInfoImpl.cs index bc9a9d33757924..bbb4cb6218896a 100644 --- a/src/coreclr/src/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/src/tools/Common/JitInterface/CorInfoImpl.cs @@ -684,21 +684,6 @@ private CorInfoType asCorInfoType(TypeDesc type, out TypeDesc typeIfNotPrimitive { throw new RequiresRuntimeJitException(type); } -#endif -#if READYTORUN - if (elementSize.AsInt == 4) - { - var normalizedCategory = _compilation.TypeSystemContext.NormalizedCategoryFor4ByteStructOnX86(type); - if (normalizedCategory != type.Category) - { - if (NeedsTypeLayoutCheck(type)) - { - ISymbolNode node = _compilation.SymbolNodeFactory.CheckTypeLayout(type); - _methodCodeNode.Fixups.Add(node); - } - return (CorInfoType)normalizedCategory; - } - } #endif } return CorInfoType.CORINFO_TYPE_VALUECLASS; diff --git a/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs b/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs index f5e324e04b6e81..de5cb85bfad428 100644 --- a/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs +++ b/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs @@ -120,17 +120,7 @@ public CorElementType GetCorElementType() return CorElementType.ELEMENT_TYPE_BYREF; } - Internal.TypeSystem.TypeFlags category; - switch (_type.Context.Target.Architecture) - { - case TargetArchitecture.X86: - category = ((CompilerTypeSystemContext)_type.Context).NormalizedCategoryFor4ByteStructOnX86(_type.UnderlyingType); - break; - - default: - category = _type.UnderlyingType.Category; - break; - } + Internal.TypeSystem.TypeFlags category = _type.UnderlyingType.Category; // We use the UnderlyingType to handle Enums properly return category switch { diff --git a/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs b/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs index fac7217077b82c..bd89459beeca96 100644 --- a/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs +++ b/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs @@ -198,6 +198,14 @@ public bool IsArgumentInRegister(ref int pNumRegistersUsed, CorElementType typ, } break; } +#elif READYTORUN + case CorElementType.ELEMENT_TYPE_VALUETYPE: + if (IsTrivialPointerSizedStruct(thArgType)) + { + pNumRegistersUsed++; + return true; + } + break; #endif } } @@ -205,6 +213,46 @@ public bool IsArgumentInRegister(ref int pNumRegistersUsed, CorElementType typ, return false; } + private bool IsTrivialPointerSizedStruct(TypeHandle thArgType) + { + Debug.Assert(IsX86); + Debug.Assert(thArgType.IsValueType()); + if (thArgType.GetSize() != 4) + { + // Type does not have trivial layout or has the wrong size. + return false; + } + TypeDesc typeOfEmbeddedField = null; + foreach (var field in thArgType.GetRuntimeTypeHandle().GetFields()) + { + if (field.IsStatic) + continue; + if (typeOfEmbeddedField != null) + { + // Type has more than one instance field + return false; + } + + typeOfEmbeddedField = field.FieldType; + } + + if ((typeOfEmbeddedField != null) && ((typeOfEmbeddedField.IsValueType) || (typeOfEmbeddedField.IsPointer))) + { + switch (typeOfEmbeddedField.UnderlyingType.Category) + { + case TypeFlags.IntPtr: + case TypeFlags.UIntPtr: + case TypeFlags.Int32: + case TypeFlags.UInt32: + case TypeFlags.Pointer: + return true; + case TypeFlags.ValueType: + return IsTrivialPointerSizedStruct(new TypeHandle(typeOfEmbeddedField)); + } + } + return false; + } + /// /// This overload should only be used in AMD64-specific code only. /// diff --git a/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilerContext.cs b/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilerContext.cs index 86b179a0264aa1..5b8c09852f7ec3 100644 --- a/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilerContext.cs +++ b/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilerContext.cs @@ -17,70 +17,6 @@ public CompilerTypeSystemContext(TargetDetails details, SharedGenericsMode gener { _genericsMode = genericsMode; } - - private object _normalizedLock = new object(); - private HashSet _nonNormalizedTypes = new HashSet(); - private Dictionary _normalizedTypeCategory = new Dictionary(); - - public TypeFlags NormalizedCategoryFor4ByteStructOnX86(TypeDesc type) - { - // Fast early out for cases which don't need normalization - var typeCategory = type.Category; - - if (((typeCategory != TypeFlags.ValueType) && (typeCategory != TypeFlags.Enum)) || (type.GetElementSize().AsInt != 4)) - { - return typeCategory; - } - - lock(_normalizedLock) - { - if (_nonNormalizedTypes.Contains(type)) - return typeCategory; - - if (_normalizedTypeCategory.TryGetValue(type, out TypeFlags category)) - return category; - - if (Target.Architecture != TargetArchitecture.X86) - { - throw new NotSupportedException(); - } - - TypeDesc typeOfEmbeddedField = null; - foreach (var field in type.GetFields()) - { - if (field.IsStatic) - continue; - if (typeOfEmbeddedField != null) - { - // Type has more than one instance field - _nonNormalizedTypes.Add(type); - return typeCategory; - } - - typeOfEmbeddedField = field.FieldType; - } - - if ((typeOfEmbeddedField != null) && ((typeOfEmbeddedField.IsValueType) || (typeOfEmbeddedField.IsPointer))) - { - TypeFlags singleElementFieldType = NormalizedCategoryFor4ByteStructOnX86(typeOfEmbeddedField); - if (singleElementFieldType == TypeFlags.Pointer) - singleElementFieldType = TypeFlags.UIntPtr; - - switch (singleElementFieldType) - { - case TypeFlags.IntPtr: - case TypeFlags.UIntPtr: - case TypeFlags.Int32: - case TypeFlags.UInt32: - _normalizedTypeCategory.Add(type, singleElementFieldType); - return singleElementFieldType; - } - } - - _nonNormalizedTypes.Add(type); - return typeCategory; - } - } } public partial class ReadyToRunCompilerContext : CompilerTypeSystemContext From 0368407a9a07e8dc6088a75bc77e1dd716607cf3 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 27 Oct 2020 14:41:35 -0700 Subject: [PATCH 49/73] Fix license headers. --- .../Interop/PInvoke/Miscellaneous/ThisCall/ThisCallNative.cpp | 1 - src/tests/Interop/PInvoke/Miscellaneous/ThisCall/ThisCallTest.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/src/tests/Interop/PInvoke/Miscellaneous/ThisCall/ThisCallNative.cpp b/src/tests/Interop/PInvoke/Miscellaneous/ThisCall/ThisCallNative.cpp index afbc606b348110..cf2569fb75386b 100644 --- a/src/tests/Interop/PInvoke/Miscellaneous/ThisCall/ThisCallNative.cpp +++ b/src/tests/Interop/PInvoke/Miscellaneous/ThisCall/ThisCallNative.cpp @@ -1,6 +1,5 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. #include #include diff --git a/src/tests/Interop/PInvoke/Miscellaneous/ThisCall/ThisCallTest.cs b/src/tests/Interop/PInvoke/Miscellaneous/ThisCall/ThisCallTest.cs index bebbdfd02d07ea..66d40546e91b36 100644 --- a/src/tests/Interop/PInvoke/Miscellaneous/ThisCall/ThisCallTest.cs +++ b/src/tests/Interop/PInvoke/Miscellaneous/ThisCall/ThisCallTest.cs @@ -1,6 +1,5 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. using System.Runtime.InteropServices; using System; From a4286f3d63a2cde089e57a564e2435fa7f018846 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 27 Oct 2020 14:42:46 -0700 Subject: [PATCH 50/73] Remove the old validation that relates only to the old x86 struct normalization model. The new model normalizes within the JIT, so the interop IL stubs don't need to account for it. Signed-off-by: Jeremy Koritzinsky --- src/coreclr/src/vm/mlinfo.cpp | 128 ---------------------------------- src/coreclr/src/vm/mlinfo.h | 7 -- 2 files changed, 135 deletions(-) diff --git a/src/coreclr/src/vm/mlinfo.cpp b/src/coreclr/src/vm/mlinfo.cpp index c3440afb4bf6ab..1097bbea2a6ad3 100644 --- a/src/coreclr/src/vm/mlinfo.cpp +++ b/src/coreclr/src/vm/mlinfo.cpp @@ -659,102 +659,6 @@ BOOL ParseNativeTypeInfo(NativeTypeParamInfo* pParamInfo, return TRUE; } -//========================================================================== -// Determines whether *pManagedElemType is really normalized (i.e. differs -// from what sigPtr points to modulo generic instantiation). If it is the -// case, all types that have been normalized away are checked for valid -// managed/unmanaged type combination, and *pNativeType is updated to contain -// the native type of the primitive type field inside. On error (a generic -// type is encountered or managed/unmanaged type mismatch) or non-default -// native type of the primitive type inside, *pManagedElemType is un-normalized -// so that the calling code can deal with the situation in its own way. -//========================================================================== -void VerifyAndAdjustNormalizedType( - Module * pModule, - SigPointer sigPtr, - const SigTypeContext * pTypeContext, - CorElementType * pManagedElemType, - CorNativeType * pNativeType) -{ - CorElementType sigElemType = sigPtr.PeekElemTypeClosed(pModule, pTypeContext); - - if (*pManagedElemType != sigElemType) - { - // Normalized element type differs from closed element type, which means that - // normalization has occurred. - _ASSERTE(sigElemType == ELEMENT_TYPE_VALUETYPE); - - // Now we know that this is a normalized value type - we have to verify the removed - // value type(s) and get to the true primitive type inside. - TypeHandle th = sigPtr.GetTypeHandleThrowing(pModule, - pTypeContext, - ClassLoader::LoadTypes, - CLASS_LOAD_UNRESTORED, - TRUE); - _ASSERTE(!th.IsNull() && !th.IsTypeDesc()); - - CorNativeType ntype = *pNativeType; - - if (!th.AsMethodTable()->IsTruePrimitive() && - !th.IsEnum()) - { - // This is a trivial (yet non-primitive) value type that has been normalized. - // Loop until we eventually hit the primitive type or enum inside. - do - { - if (th.HasInstantiation()) - { - // generic structures are either not marshalable or special-cased - the caller needs to know either way - *pManagedElemType = sigElemType; - return; - } - - // verify the native type of the value type (must be default or Struct) - if (!(ntype == NATIVE_TYPE_DEFAULT || ntype == NATIVE_TYPE_STRUCT)) - { - *pManagedElemType = sigElemType; - return; - } - - MethodTable *pMT = th.GetMethodTable(); - _ASSERTE(pMT != NULL && pMT->IsValueType() && pMT->GetNumInstanceFields() == 1); - - // get the only instance field - PTR_FieldDesc fieldDesc = pMT->GetApproxFieldDescListRaw(); - - // retrieve the MarshalAs of the field - NativeTypeParamInfo paramInfo; - if (!ParseNativeTypeInfo(fieldDesc->GetMemberDef(), th.GetModule()->GetMDImport(), ¶mInfo)) - { - *pManagedElemType = sigElemType; - return; - } - - ntype = paramInfo.m_NativeType; - - th = fieldDesc->GetApproxFieldTypeHandleThrowing(); - } - while (!th.IsTypeDesc() && - !th.AsMethodTable()->IsTruePrimitive() && - !th.IsEnum()); - - // now ntype contains the native type of *pManagedElemType - if (ntype == NATIVE_TYPE_DEFAULT) - { - // Let's update the caller's native type with default type only. - // Updating with a non-default native type that is not allowed - // for the given managed type would result in confusing exception - // messages. - *pNativeType = ntype; - } - else - { - *pManagedElemType = sigElemType; - } - } - } -} - VOID ThrowInteropParamException(UINT resID, UINT paramIdx) { CONTRACTL @@ -1365,38 +1269,6 @@ MarshalInfo::MarshalInfo(Module* pModule, } } - // System primitive types (System.Int32, et.al.) will be marshaled as expected - // because the mtype CorElementType is normalized (e.g. ELEMENT_TYPE_I4). -#ifdef TARGET_X86 - // We however need to detect if such a normalization occurred for non-system - // trivial value types, because we hold CorNativeType belonging to the original - // "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. - // We don't normalize on return values of member functions since struct return values need to be treated as structures. - if (isParam) - { - VerifyAndAdjustNormalizedType(pModule, sig, pTypeContext, &mtype, &nativeType); - } - else - { - SigPointer sigtmp = sig; - CorElementType closedElemType = sigtmp.PeekElemTypeClosed(pModule, pTypeContext); - if (closedElemType == ELEMENT_TYPE_VALUETYPE) - { - TypeHandle th = sigtmp.GetTypeHandleThrowing(pModule, pTypeContext); - // If the return type of an instance method is a value-type we need the actual return type. - // However, if the return type is an enum, we can normalize it. - if (!th.IsEnum()) - { - mtype = closedElemType; - } - } - - } -#endif // TARGET_X86 - - if (nativeType == NATIVE_TYPE_CUSTOMMARSHALER) { if (IsFieldScenario()) diff --git a/src/coreclr/src/vm/mlinfo.h b/src/coreclr/src/vm/mlinfo.h index ff435d1b9d6d40..792d078220f563 100644 --- a/src/coreclr/src/vm/mlinfo.h +++ b/src/coreclr/src/vm/mlinfo.h @@ -188,13 +188,6 @@ BOOL ParseNativeTypeInfo(mdToken token, IMDInternalImport* pScope, NativeTypeParamInfo* pParamInfo); -void VerifyAndAdjustNormalizedType( - Module * pModule, - SigPointer sigPtr, - const SigTypeContext * pTypeContext, - CorElementType * pManagedElemType, - CorNativeType * pNativeType); - #ifdef _DEBUG BOOL IsFixedBuffer(mdFieldDef field, IMDInternalImport* pInternalImport); #endif From bae4165a3fa3a9c557fbeb5c85c49bd34a1ec9cd Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 27 Oct 2020 17:10:24 -0700 Subject: [PATCH 51/73] Apply suggestions from code review Co-authored-by: Carol Eidt --- src/coreclr/src/jit/compiler.h | 4 ++-- src/coreclr/src/jit/importer.cpp | 3 ++- src/coreclr/src/jit/morph.cpp | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/coreclr/src/jit/compiler.h b/src/coreclr/src/jit/compiler.h index f235a779830749..a35e6de462929c 100644 --- a/src/coreclr/src/jit/compiler.h +++ b/src/coreclr/src/jit/compiler.h @@ -9353,7 +9353,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX { #if FEATURE_MULTIREG_RET #if defined(TARGET_X86) - // On x86, 64-bit longs are returned in multiple registers + // On x86, 64-bit longs and some structs are returned in multiple registers return varTypeIsLong(info.compRetNativeType) || (varTypeIsStruct(info.compRetNativeType) && (info.compRetBuffArg == BAD_VAR_NUM)); #else // targets: X64-UNIX, ARM64 or ARM32 @@ -9536,7 +9536,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX // size of the type these describe. unsigned compGetTypeSize(CorInfoType cit, CORINFO_CLASS_HANDLE clsHnd); - // Calculates if this method's entry point should have the same calling + // Determines whether this method's entry point should have the same calling // convention as an unmanaged instance method variant of the standard calling convention. // (only used on applicable platforms) bool compMethodIsNativeInstanceMethod(CORINFO_METHOD_INFO* mthInfo); diff --git a/src/coreclr/src/jit/importer.cpp b/src/coreclr/src/jit/importer.cpp index 5ad38d235caf3a..ba6ec52e4d5cf8 100644 --- a/src/coreclr/src/jit/importer.cpp +++ b/src/coreclr/src/jit/importer.cpp @@ -1178,7 +1178,8 @@ GenTree* Compiler::impAssignStructPtr(GenTree* destAddr, GenTreeCall* srcCall = src->AsCall(); if (srcCall->TreatAsHasRetBufArg(this)) { -// Case of call returning a struct via hidden retbuf arg + // Case of call returning a struct via hidden retbuf arg + CLANG_FORMAT_COMMENT_ANCHOR; #if defined(TARGET_WINDOWS) && !defined(TARGET_ARM) // Unmanaged instance methods on Windows need the retbuf arg after the first (this) parameter diff --git a/src/coreclr/src/jit/morph.cpp b/src/coreclr/src/jit/morph.cpp index 3e510277a76b8e..a5df0e0865359d 100644 --- a/src/coreclr/src/jit/morph.cpp +++ b/src/coreclr/src/jit/morph.cpp @@ -2677,7 +2677,7 @@ void Compiler::fgInitArgInfo(GenTreeCall* call) CLANG_FORMAT_COMMENT_ANCHOR; #if defined(TARGET_WINDOWS) && !defined(TARGET_ARM) - if (hasFixedRetBuffReg() && call->HasRetBufArg() && (call->gtCallMoreFlags & GTF_CALL_M_UNMGD_INST_CALL) == 0) + if (hasFixedRetBuffReg() && call->HasRetBufArg() && ((call->gtCallMoreFlags & GTF_CALL_M_UNMGD_INST_CALL) == 0)) #else if (hasFixedRetBuffReg() && call->HasRetBufArg()) #endif From 77d6bf64ed4fffdc69412b0986214a99fd421763 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 28 Oct 2020 11:20:45 -0700 Subject: [PATCH 52/73] Add more comments and clean up the code --- src/coreclr/src/jit/codegencommon.cpp | 7 +++++-- src/coreclr/src/jit/compiler.cpp | 8 ++++---- src/coreclr/src/jit/importer.cpp | 2 +- src/coreclr/src/jit/lclvars.cpp | 22 +++++++++++++++++++++- 4 files changed, 31 insertions(+), 8 deletions(-) diff --git a/src/coreclr/src/jit/codegencommon.cpp b/src/coreclr/src/jit/codegencommon.cpp index 88307a5c374ebe..f1007d89cf007a 100644 --- a/src/coreclr/src/jit/codegencommon.cpp +++ b/src/coreclr/src/jit/codegencommon.cpp @@ -11529,9 +11529,11 @@ void CodeGen::genReturn(GenTree* treeNode) } else // we must have a struct return type { + CorInfoUnmanagedCallConv callConv = compiler->compMethodInfoGetUnmanagedCallConv( + compiler->info.compMethodInfo); + retTypeDesc.InitializeStructReturnType(compiler, compiler->info.compMethodInfo->args.retTypeClass, - compiler->compMethodInfoGetUnmanagedCallConv( - compiler->info.compMethodInfo)); + callConv); } regCount = retTypeDesc.GetReturnRegCount(); } @@ -11838,6 +11840,7 @@ void CodeGen::genMultiRegStoreToLocal(GenTreeLclVar* lclNode) hasRegs = true; if (varReg != reg) { + // We may need a cross register-file copy here. inst_RV_RV(ins_Copy(reg, type), varReg, reg, type); } fieldVarDsc->SetRegNum(varReg); diff --git a/src/coreclr/src/jit/compiler.cpp b/src/coreclr/src/jit/compiler.cpp index a2c4d9d44eb031..ecf6a26c060aa3 100644 --- a/src/coreclr/src/jit/compiler.cpp +++ b/src/coreclr/src/jit/compiler.cpp @@ -930,6 +930,8 @@ var_types Compiler::getArgTypeForStruct(CORINFO_CLASS_HANDLE clsHnd, // // Arguments: // clsHnd - the handle for the struct type +// callConv - the calling convention of the function +// that returns this struct. // wbReturnStruct - An "out" argument with information about how // the struct is to be returned // structSize - the size of the struct type, @@ -1140,10 +1142,8 @@ var_types Compiler::getReturnTypeForStruct(CORINFO_CLASS_HANDLE clsHnd, #elif defined(TARGET_X86) // Only 8-byte structs are return in multiple registers. - // We also only support multireg struct returns - // on x86 to match the native calling convention. - // So only do it for native calling conventions that aren't - // instance methods + // We also only support multireg struct returns on x86 to match the native calling convention. + // So return 8-byte structs only when the calling convention is a native calling convention. if (structSize == MAX_RET_MULTIREG_BYTES && callConv != CORINFO_UNMANAGED_CALLCONV_UNKNOWN) { // setup wbPassType and useType indicate that this is return by value in multiple registers diff --git a/src/coreclr/src/jit/importer.cpp b/src/coreclr/src/jit/importer.cpp index ba6ec52e4d5cf8..403dc761b57d6c 100644 --- a/src/coreclr/src/jit/importer.cpp +++ b/src/coreclr/src/jit/importer.cpp @@ -9353,7 +9353,7 @@ GenTree* Compiler::impFixupStructReturnType(GenTree* op, if (op->gtOper == GT_LCL_VAR) { - // Make sure that this struct stays in memory and doesn't get promoted. + // Note that this is a multi-reg return. unsigned lclNum = op->AsLclVarCommon()->GetLclNum(); lvaTable[lclNum].lvIsMultiRegRet = true; diff --git a/src/coreclr/src/jit/lclvars.cpp b/src/coreclr/src/jit/lclvars.cpp index d5314cefceb9b6..e26f73b4f80f6e 100644 --- a/src/coreclr/src/jit/lclvars.cpp +++ b/src/coreclr/src/jit/lclvars.cpp @@ -576,7 +576,19 @@ void Compiler::lvaInitRetBuffArg(InitVarDscInfo* varDscInfo, bool useFixedRetBuf } } -/*****************************************************************************/ + +//----------------------------------------------------------------------------- +// getReturnTypeForStruct: +// Get the type that is used to return values of the given struct type. +// If you have already retrieved the struct size then it should be +// passed as the optional third argument, as this allows us to avoid +// an extra call to getClassSize(clsHnd) +// +// Arguments: +// varDscInfo - the local var descriptions +// skipArgs - the number of user args to skip processing. +// takeArgs - the number of user args to process (after skipping skipArgs number of args) +// void Compiler::lvaInitUserArgs(InitVarDscInfo* varDscInfo, unsigned skipArgs, unsigned takeArgs) { //------------------------------------------------------------------------- @@ -595,8 +607,10 @@ void Compiler::lvaInitUserArgs(InitVarDscInfo* varDscInfo, unsigned skipArgs, un const unsigned argSigLen = info.compMethodInfo->args.numArgs; + // We will process at most takeArgs arguments from the signature after skipping skipArgs arguments const int64_t numUserArgs = min(takeArgs, (argSigLen - (int64_t)skipArgs)); + // If there are no user args or less than skipArgs args, return here since there's no work to do. if (numUserArgs <= 0) { return; @@ -606,6 +620,7 @@ void Compiler::lvaInitUserArgs(InitVarDscInfo* varDscInfo, unsigned skipArgs, un regMaskTP doubleAlignMask = RBM_NONE; #endif // TARGET_ARM + // Skip skipArgs arguments from the signature. for (unsigned i = 0; i < skipArgs; i++, argLst = info.compCompHnd->getArgNext(argLst)) { ; @@ -5332,6 +5347,10 @@ void Compiler::lvaAssignVirtualFrameOffsetsToArgs() unsigned userArgsToSkip = 0; #if defined(TARGET_WINDOWS) && !defined(TARGET_ARM) + // In the native instance method calling convention on Windows, + // 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 (compMethodIsNativeInstanceMethod(info.compMethodInfo)) { noway_assert(lvaTable[lclNum].lvIsRegArg); @@ -5376,6 +5395,7 @@ void Compiler::lvaAssignVirtualFrameOffsetsToArgs() CORINFO_ARG_LIST_HANDLE argLst = info.compMethodInfo->args.args; unsigned argSigLen = info.compMethodInfo->args.numArgs; + // Skip any user args that we've already processed. assert(userArgsToSkip <= argSigLen); argSigLen -= userArgsToSkip; for (unsigned i = 0; i < userArgsToSkip; i++, argLst = info.compCompHnd->getArgNext(argLst)) From 9e965f813508e7b518503c9a127c6ff43758a43d Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 28 Oct 2020 11:30:21 -0700 Subject: [PATCH 53/73] introduce a CORINFO_UNMANAGED_CALLCONV_MANAGED enum member to represent the managed calling convention. Signed-off-by: Jeremy Koritzinsky --- src/coreclr/src/inc/corinfo.h | 3 ++- src/coreclr/src/jit/compiler.cpp | 7 ++++--- src/coreclr/src/jit/compiler.h | 2 +- src/coreclr/src/jit/compiler.hpp | 2 +- src/coreclr/src/jit/flowgraph.cpp | 4 ++-- src/coreclr/src/jit/gentree.cpp | 2 +- src/coreclr/src/jit/importer.cpp | 2 +- 7 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/coreclr/src/inc/corinfo.h b/src/coreclr/src/inc/corinfo.h index e1ddd171957458..e63738db913364 100644 --- a/src/coreclr/src/inc/corinfo.h +++ b/src/coreclr/src/inc/corinfo.h @@ -742,7 +742,8 @@ enum CorInfoUnmanagedCallConv CORINFO_UNMANAGED_CALLCONV_C, CORINFO_UNMANAGED_CALLCONV_STDCALL, CORINFO_UNMANAGED_CALLCONV_THISCALL, - CORINFO_UNMANAGED_CALLCONV_FASTCALL + CORINFO_UNMANAGED_CALLCONV_FASTCALL, + CORINFO_UNMANAGED_CALLCONV_MANAGED }; // These are returned from getMethodOptions diff --git a/src/coreclr/src/jit/compiler.cpp b/src/coreclr/src/jit/compiler.cpp index ecf6a26c060aa3..3b8f6ee97b45f9 100644 --- a/src/coreclr/src/jit/compiler.cpp +++ b/src/coreclr/src/jit/compiler.cpp @@ -1018,7 +1018,7 @@ var_types Compiler::getReturnTypeForStruct(CORINFO_CLASS_HANDLE clsHnd, #endif // UNIX_AMD64_ABI #ifdef UNIX_X86_ABI - if (callConv != CORINFO_UNMANAGED_CALLCONV_UNKNOWN) + if (callConv != CORINFO_UNMANAGED_CALLCONV_MANAGED) { canReturnInRegister = false; howToReturnStruct = SPK_ByReference; @@ -1144,7 +1144,7 @@ var_types Compiler::getReturnTypeForStruct(CORINFO_CLASS_HANDLE clsHnd, // Only 8-byte structs are return in multiple registers. // We also only support multireg struct returns on x86 to match the native calling convention. // So return 8-byte structs only when the calling convention is a native calling convention. - if (structSize == MAX_RET_MULTIREG_BYTES && callConv != CORINFO_UNMANAGED_CALLCONV_UNKNOWN) + if (structSize == MAX_RET_MULTIREG_BYTES && callConv != CORINFO_UNMANAGED_CALLCONV_MANAGED) { // setup wbPassType and useType indicate that this is return by value in multiple registers howToReturnStruct = SPK_ByValue; @@ -2071,7 +2071,8 @@ CorInfoUnmanagedCallConv Compiler::compMethodInfoGetUnmanagedCallConv(CORINFO_ME CorInfoCallConv callConv = mthInfo->args.getCallConv(); if (callConv == CORINFO_CALLCONV_DEFAULT || callConv == CORINFO_CALLCONV_VARARG) { - return CORINFO_UNMANAGED_CALLCONV_UNKNOWN; + // Both the default and the varargs calling conventions represent a managed callconv. + return CORINFO_UNMANAGED_CALLCONV_MANAGED; } return (CorInfoUnmanagedCallConv)callConv; } diff --git a/src/coreclr/src/jit/compiler.h b/src/coreclr/src/jit/compiler.h index a35e6de462929c..b35cd3d7204df5 100644 --- a/src/coreclr/src/jit/compiler.h +++ b/src/coreclr/src/jit/compiler.h @@ -9542,7 +9542,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX bool compMethodIsNativeInstanceMethod(CORINFO_METHOD_INFO* mthInfo); // Gets the unmanaged calling convention the method's entry point should have. - // Returns CorInfoUnmanagedCallConv::CORINFO_UNMANAGED_CALLCONV_UNKNOWN for the managed + // Returns CorInfoUnmanagedCallConv::CORINFO_UNMANAGED_CALLCONV_MANAGED for the managed // calling convention. CorInfoUnmanagedCallConv compMethodInfoGetUnmanagedCallConv(CORINFO_METHOD_INFO* mthInfo); diff --git a/src/coreclr/src/jit/compiler.hpp b/src/coreclr/src/jit/compiler.hpp index 4ab33fe340c428..7b3f9d5aac670f 100644 --- a/src/coreclr/src/jit/compiler.hpp +++ b/src/coreclr/src/jit/compiler.hpp @@ -693,7 +693,7 @@ inline bool isRegParamType(var_types type) // - if so on arm64 windows getArgTypeForStruct will ignore HFA // - types // callConv - the unmanaged calling convention of the call, -// - or CORINFO_UNMANAGED_CALLCONV_UNKNOWN for the +// - or CORINFO_UNMANAGED_CALLCONV_MANAGED for the // - managed calling convention. // inline bool Compiler::VarTypeIsMultiByteAndCanEnreg(var_types type, diff --git a/src/coreclr/src/jit/flowgraph.cpp b/src/coreclr/src/jit/flowgraph.cpp index 32f00cf21d3336..9c460ed2f1f8aa 100644 --- a/src/coreclr/src/jit/flowgraph.cpp +++ b/src/coreclr/src/jit/flowgraph.cpp @@ -22826,7 +22826,7 @@ Compiler::fgWalkResult Compiler::fgUpdateInlineReturnExpressionPlaceHolder(GenTr { structPassingKind howToReturnStruct; var_types returnType = - comp->getReturnTypeForStruct(retClsHnd, CORINFO_UNMANAGED_CALLCONV_UNKNOWN, &howToReturnStruct); + comp->getReturnTypeForStruct(retClsHnd, CORINFO_UNMANAGED_CALLCONV_MANAGED, &howToReturnStruct); GenTree* parent = data->parent; switch (howToReturnStruct) @@ -22941,7 +22941,7 @@ Compiler::fgWalkResult Compiler::fgUpdateInlineReturnExpressionPlaceHolder(GenTr noway_assert(!varTypeIsStruct(effectiveValue) || (effectiveValue->OperGet() != GT_RET_EXPR) || !comp->IsMultiRegReturnedType(effectiveValue->AsRetExpr()->gtRetClsHnd, - CORINFO_UNMANAGED_CALLCONV_UNKNOWN)); + CORINFO_UNMANAGED_CALLCONV_MANAGED)); } } diff --git a/src/coreclr/src/jit/gentree.cpp b/src/coreclr/src/jit/gentree.cpp index fea5193698f731..2b9b7d52bceb8c 100644 --- a/src/coreclr/src/jit/gentree.cpp +++ b/src/coreclr/src/jit/gentree.cpp @@ -6252,7 +6252,7 @@ GenTreeCall* Compiler::gtNewCallNode( node->gtRetClsHnd = nullptr; node->gtControlExpr = nullptr; node->gtCallMoreFlags = 0; - node->unmgdCallConv = CORINFO_UNMANAGED_CALLCONV_UNKNOWN; + node->unmgdCallConv = CORINFO_UNMANAGED_CALLCONV_MANAGED; if (callType == CT_INDIRECT) { diff --git a/src/coreclr/src/jit/importer.cpp b/src/coreclr/src/jit/importer.cpp index 403dc761b57d6c..f5d76fcec29f8e 100644 --- a/src/coreclr/src/jit/importer.cpp +++ b/src/coreclr/src/jit/importer.cpp @@ -15786,7 +15786,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) #if FEATURE_MULTIREG_RET if (varTypeIsStruct(op1) && - IsMultiRegReturnedType(resolvedToken.hClass, CORINFO_UNMANAGED_CALLCONV_UNKNOWN)) + IsMultiRegReturnedType(resolvedToken.hClass, CORINFO_UNMANAGED_CALLCONV_MANAGED)) { // Unbox nullable helper returns a TYP_STRUCT. // For the multi-reg case we need to spill it to a temp so that From c28925efeb58296dca750b44773e1e7cafd51167 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 28 Oct 2020 14:01:02 -0700 Subject: [PATCH 54/73] Fix formatting --- src/coreclr/src/jit/codegencommon.cpp | 6 +++--- src/coreclr/src/jit/lclvars.cpp | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/coreclr/src/jit/codegencommon.cpp b/src/coreclr/src/jit/codegencommon.cpp index f1007d89cf007a..bbefe1e8985d7a 100644 --- a/src/coreclr/src/jit/codegencommon.cpp +++ b/src/coreclr/src/jit/codegencommon.cpp @@ -11529,9 +11529,9 @@ void CodeGen::genReturn(GenTree* treeNode) } else // we must have a struct return type { - CorInfoUnmanagedCallConv callConv = compiler->compMethodInfoGetUnmanagedCallConv( - compiler->info.compMethodInfo); - + CorInfoUnmanagedCallConv callConv = + compiler->compMethodInfoGetUnmanagedCallConv(compiler->info.compMethodInfo); + retTypeDesc.InitializeStructReturnType(compiler, compiler->info.compMethodInfo->args.retTypeClass, callConv); } diff --git a/src/coreclr/src/jit/lclvars.cpp b/src/coreclr/src/jit/lclvars.cpp index e26f73b4f80f6e..bb56dd1c3766dd 100644 --- a/src/coreclr/src/jit/lclvars.cpp +++ b/src/coreclr/src/jit/lclvars.cpp @@ -576,7 +576,6 @@ void Compiler::lvaInitRetBuffArg(InitVarDscInfo* varDscInfo, bool useFixedRetBuf } } - //----------------------------------------------------------------------------- // getReturnTypeForStruct: // Get the type that is used to return values of the given struct type. From e5212cb5e11674ffc35bf18db8fd32cefa798303 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 29 Oct 2020 17:10:39 -0700 Subject: [PATCH 55/73] Apply suggestions from code review Co-authored-by: Sergey Andreenko --- src/coreclr/src/jit/compiler.cpp | 3 +-- src/coreclr/src/jit/compiler.h | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/coreclr/src/jit/compiler.cpp b/src/coreclr/src/jit/compiler.cpp index 3b8f6ee97b45f9..6e48e895e8dcf4 100644 --- a/src/coreclr/src/jit/compiler.cpp +++ b/src/coreclr/src/jit/compiler.cpp @@ -518,13 +518,12 @@ bool Compiler::isSingleFloat32Struct(CORINFO_CLASS_HANDLE clsHnd) // Check if the given struct type contains only one pointer-sized integer value type // // Arguments: -// clsHnd - the handle for the struct type +// clsHnd - the handle for the struct type. // // Return Value: // true if the given struct type contains only one pointer-sized integer value type, // false otherwise. // - bool Compiler::isTrivialPointerSizedStruct(CORINFO_CLASS_HANDLE clsHnd) { assert(info.compCompHnd->isValueClass(clsHnd)); diff --git a/src/coreclr/src/jit/compiler.h b/src/coreclr/src/jit/compiler.h index b35cd3d7204df5..219fc2a1d63b17 100644 --- a/src/coreclr/src/jit/compiler.h +++ b/src/coreclr/src/jit/compiler.h @@ -9353,7 +9353,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX { #if FEATURE_MULTIREG_RET #if defined(TARGET_X86) - // On x86, 64-bit longs and some structs are returned in multiple registers + // On x86, 64-bit longs and structs are returned in multiple registers return varTypeIsLong(info.compRetNativeType) || (varTypeIsStruct(info.compRetNativeType) && (info.compRetBuffArg == BAD_VAR_NUM)); #else // targets: X64-UNIX, ARM64 or ARM32 @@ -9379,7 +9379,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX { #if FEATURE_MULTIREG_RET #if defined(TARGET_X86) - // On x86, 64-bit longs are returned in multiple registers + // On x86, 64-bit longs and structs are returned in multiple registers return varTypeIsLong(info.compRetNativeType) || (varTypeIsStruct(info.compRetNativeType) && (info.compRetBuffArg == BAD_VAR_NUM)); #else // targets: X64-UNIX, ARM64 or ARM32 From e02016552b0b7bb3cac05b16df8391bb492daa9c Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 2 Nov 2020 11:13:40 -0800 Subject: [PATCH 56/73] PR feedback. --- src/coreclr/src/jit/codegenxarch.cpp | 9 +++++---- src/coreclr/src/jit/compiler.cpp | 6 +++--- src/coreclr/src/jit/compiler.h | 10 ++++------ src/coreclr/src/jit/gentree.h | 2 -- src/coreclr/src/jit/importer.cpp | 5 ++--- src/coreclr/src/jit/lclvars.cpp | 4 ++-- src/coreclr/src/jit/morph.cpp | 2 +- 7 files changed, 17 insertions(+), 21 deletions(-) diff --git a/src/coreclr/src/jit/codegenxarch.cpp b/src/coreclr/src/jit/codegenxarch.cpp index 7db42d5f964d2a..1a1b92ec98283c 100644 --- a/src/coreclr/src/jit/codegenxarch.cpp +++ b/src/coreclr/src/jit/codegenxarch.cpp @@ -5725,12 +5725,13 @@ void CodeGen::genJmpMethod(GenTree* jmp) var_types loadType = varDsc->lvaArgType(); #ifdef TARGET_X86 - if (varTypeIsStruct(varDsc->TypeGet()) && compiler->isTrivialPointerSizedStruct(varDsc->GetStructHnd())) + if (varTypeIsStruct(varDsc->TypeGet())) { - // Treat trivial pointer-sized structs as a pointer sized primitive - // for the purposes of registers. - loadType = TYP_I_IMPL; + assert(compiler->isTrivialPointerSizedStruct(varDsc->GetStructHnd())); } + // Treat trivial pointer-sized structs as a pointer sized primitive + // for the purposes of registers. + loadType = TYP_I_IMPL; #endif regNumber argReg = varDsc->GetArgReg(); // incoming arg register diff --git a/src/coreclr/src/jit/compiler.cpp b/src/coreclr/src/jit/compiler.cpp index 6e48e895e8dcf4..163ddc630e733b 100644 --- a/src/coreclr/src/jit/compiler.cpp +++ b/src/coreclr/src/jit/compiler.cpp @@ -524,7 +524,7 @@ bool Compiler::isSingleFloat32Struct(CORINFO_CLASS_HANDLE clsHnd) // true if the given struct type contains only one pointer-sized integer value type, // false otherwise. // -bool Compiler::isTrivialPointerSizedStruct(CORINFO_CLASS_HANDLE clsHnd) +bool Compiler::isTrivialPointerSizedStruct(CORINFO_CLASS_HANDLE clsHnd) const { assert(info.compCompHnd->isValueClass(clsHnd)); if (info.compCompHnd->getClassSize(clsHnd) != TARGET_POINTER_SIZE) @@ -2060,9 +2060,9 @@ unsigned Compiler::compGetTypeSize(CorInfoType cit, CORINFO_CLASS_HANDLE clsHnd) return sigSize; } -bool Compiler::compMethodIsNativeInstanceMethod(CORINFO_METHOD_INFO* mthInfo) +bool Compiler::callConvIsInstanceMethodCallConv(CorInfoUnmanagedCallConv callConv) { - return mthInfo->args.getCallConv() == CORINFO_CALLCONV_THISCALL; + return callConv == CORINFO_CALLCONV_THISCALL; } CorInfoUnmanagedCallConv Compiler::compMethodInfoGetUnmanagedCallConv(CORINFO_METHOD_INFO* mthInfo) diff --git a/src/coreclr/src/jit/compiler.h b/src/coreclr/src/jit/compiler.h index 219fc2a1d63b17..2d281d7296a6f9 100644 --- a/src/coreclr/src/jit/compiler.h +++ b/src/coreclr/src/jit/compiler.h @@ -2255,7 +2255,7 @@ class Compiler #endif // ARM_SOFTFP #ifdef TARGET_X86 - bool isTrivialPointerSizedStruct(CORINFO_CLASS_HANDLE clsHnd); + bool isTrivialPointerSizedStruct(CORINFO_CLASS_HANDLE clsHnd) const; #endif // TARGET_X86 //------------------------------------------------------------------------- @@ -9334,7 +9334,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #ifdef TARGET_AMD64 return (info.compRetBuffArg != BAD_VAR_NUM); #elif defined(TARGET_WINDOWS) && defined(TARGET_ARM64) - return (compMethodIsNativeInstanceMethod(info.compMethodInfo) || compIsProfilerHookNeeded()) && + return (callConvIsInstanceMethodCallConv(info.compMethodInfo->args.getCallConv()) || compIsProfilerHookNeeded()) && (info.compRetBuffArg != BAD_VAR_NUM); #else // !TARGET_AMD64 return (compIsProfilerHookNeeded()) && (info.compRetBuffArg != BAD_VAR_NUM); @@ -9536,10 +9536,8 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX // size of the type these describe. unsigned compGetTypeSize(CorInfoType cit, CORINFO_CLASS_HANDLE clsHnd); - // Determines whether this method's entry point should have the same calling - // convention as an unmanaged instance method variant of the standard calling convention. - // (only used on applicable platforms) - bool compMethodIsNativeInstanceMethod(CORINFO_METHOD_INFO* mthInfo); + // Determines whether or not this calling convention is an instance method calling convention. + static bool callConvIsInstanceMethodCallConv(CorInfoUnmanagedCallConv callConv); // Gets the unmanaged calling convention the method's entry point should have. // Returns CorInfoUnmanagedCallConv::CORINFO_UNMANAGED_CALLCONV_MANAGED for the managed diff --git a/src/coreclr/src/jit/gentree.h b/src/coreclr/src/jit/gentree.h index 9011864b663be7..d8c02386830c6c 100644 --- a/src/coreclr/src/jit/gentree.h +++ b/src/coreclr/src/jit/gentree.h @@ -4204,8 +4204,6 @@ struct GenTreeCall final : public GenTree #define GTF_CALL_M_SUPPRESS_GC_TRANSITION 0x00800000 // GT_CALL -- suppress the GC transition (i.e. during a pinvoke) but a separate GC safe point is required. #define GTF_CALL_M_EXP_RUNTIME_LOOKUP 0x01000000 // GT_CALL -- this call needs to be tranformed into CFG for the dynamic dictionary expansion feature. #define GTF_CALL_M_STRESS_TAILCALL 0x02000000 // GT_CALL -- the call is NOT "tail" prefixed but GTF_CALL_M_EXPLICIT_TAILCALL was added because of tail call stress mode -#define GTF_CALL_M_UNMGD_INST_CALL 0x04000000 // GT_CALL -- this call is a call to an unmanaged instance method - // (only for GTF_CALL_UNMANAGED) // clang-format on diff --git a/src/coreclr/src/jit/importer.cpp b/src/coreclr/src/jit/importer.cpp index f5d76fcec29f8e..d5600d677362fa 100644 --- a/src/coreclr/src/jit/importer.cpp +++ b/src/coreclr/src/jit/importer.cpp @@ -1185,7 +1185,7 @@ GenTree* Compiler::impAssignStructPtr(GenTree* destAddr, // Unmanaged instance methods on Windows need the retbuf arg after the first (this) parameter if (srcCall->IsUnmanaged()) { - if (srcCall->gtCallMoreFlags & GTF_CALL_M_UNMGD_INST_CALL) + if (callConvIsInstanceMethodCallConv(srcCall->unmgdCallConv)) { GenTreeCall::Use* thisArg = gtInsertNewCallArgAfter(destAddr, srcCall->gtCallArgs); } @@ -6956,7 +6956,6 @@ void Compiler::impCheckForPInvokeCall( if (unmanagedCallConv == CORINFO_UNMANAGED_CALLCONV_THISCALL) { call->gtCallMoreFlags |= GTF_CALL_M_UNMGD_THISCALL; - call->gtCallMoreFlags |= GTF_CALL_M_UNMGD_INST_CALL; } } @@ -17177,7 +17176,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 (compMethodIsNativeInstanceMethod(info.compMethodInfo)) + else if (callConvIsInstanceMethodCallConv(compMethodInfoGetUnmanagedCallConv(info.compMethodInfo))) { op1 = gtNewOperNode(GT_RETURN, TYP_BYREF, gtNewLclvNode(info.compRetBuffArg, TYP_BYREF)); } diff --git a/src/coreclr/src/jit/lclvars.cpp b/src/coreclr/src/jit/lclvars.cpp index bb56dd1c3766dd..a93d09bb299c17 100644 --- a/src/coreclr/src/jit/lclvars.cpp +++ b/src/coreclr/src/jit/lclvars.cpp @@ -353,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 (compMethodIsNativeInstanceMethod(info.compMethodInfo)) + if (callConvIsInstanceMethodCallConv(compMethodInfoGetUnmanagedCallConv(info.compMethodInfo))) { // If we are a native instance method, handle the first user arg // (the unmanaged this parameter) and then handle the hidden @@ -5350,7 +5350,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 (compMethodIsNativeInstanceMethod(info.compMethodInfo)) + if (callConvIsInstanceMethodCallConv(compMethodInfoGetUnmanagedCallConv(info.compMethodInfo))) { noway_assert(lvaTable[lclNum].lvIsRegArg); #ifndef TARGET_X86 diff --git a/src/coreclr/src/jit/morph.cpp b/src/coreclr/src/jit/morph.cpp index a5df0e0865359d..63d34a5bd50335 100644 --- a/src/coreclr/src/jit/morph.cpp +++ b/src/coreclr/src/jit/morph.cpp @@ -2677,7 +2677,7 @@ void Compiler::fgInitArgInfo(GenTreeCall* call) CLANG_FORMAT_COMMENT_ANCHOR; #if defined(TARGET_WINDOWS) && !defined(TARGET_ARM) - if (hasFixedRetBuffReg() && call->HasRetBufArg() && ((call->gtCallMoreFlags & GTF_CALL_M_UNMGD_INST_CALL) == 0)) + if (hasFixedRetBuffReg() && call->HasRetBufArg() && !callConvIsInstanceMethodCallConv(call->unmgdCallConv)) #else if (hasFixedRetBuffReg() && call->HasRetBufArg()) #endif From 90cfcefe223ef70545db9d70783ca4d433ef48e0 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 2 Nov 2020 11:29:35 -0800 Subject: [PATCH 57/73] Fix comparison. --- src/coreclr/src/jit/compiler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/src/jit/compiler.cpp b/src/coreclr/src/jit/compiler.cpp index 163ddc630e733b..2d1419060f9b21 100644 --- a/src/coreclr/src/jit/compiler.cpp +++ b/src/coreclr/src/jit/compiler.cpp @@ -2062,7 +2062,7 @@ unsigned Compiler::compGetTypeSize(CorInfoType cit, CORINFO_CLASS_HANDLE clsHnd) bool Compiler::callConvIsInstanceMethodCallConv(CorInfoUnmanagedCallConv callConv) { - return callConv == CORINFO_CALLCONV_THISCALL; + return callConv == CORINFO_UNMANAGED_CALLCONV_THISCALL; } CorInfoUnmanagedCallConv Compiler::compMethodInfoGetUnmanagedCallConv(CORINFO_METHOD_INFO* mthInfo) From 41b21a5cc6ccd16be3153eaddc4a325bbc593887 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 2 Nov 2020 12:21:14 -0800 Subject: [PATCH 58/73] fix callconv calculation. --- src/coreclr/src/jit/compiler.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/src/jit/compiler.h b/src/coreclr/src/jit/compiler.h index 2d281d7296a6f9..2d4245cf9eed09 100644 --- a/src/coreclr/src/jit/compiler.h +++ b/src/coreclr/src/jit/compiler.h @@ -9334,7 +9334,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #ifdef TARGET_AMD64 return (info.compRetBuffArg != BAD_VAR_NUM); #elif defined(TARGET_WINDOWS) && defined(TARGET_ARM64) - return (callConvIsInstanceMethodCallConv(info.compMethodInfo->args.getCallConv()) || compIsProfilerHookNeeded()) && + return (callConvIsInstanceMethodCallConv(compMethodInfoGetUnmanagedCallConv(info)) || compIsProfilerHookNeeded()) && (info.compRetBuffArg != BAD_VAR_NUM); #else // !TARGET_AMD64 return (compIsProfilerHookNeeded()) && (info.compRetBuffArg != BAD_VAR_NUM); From f6eeddf2714da01faf0de2821626959f56e84f73 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 2 Nov 2020 14:33:01 -0800 Subject: [PATCH 59/73] Fix build break. --- src/coreclr/src/jit/compiler.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/src/jit/compiler.h b/src/coreclr/src/jit/compiler.h index 2d4245cf9eed09..fdc7ada0ecf54d 100644 --- a/src/coreclr/src/jit/compiler.h +++ b/src/coreclr/src/jit/compiler.h @@ -9334,7 +9334,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #ifdef TARGET_AMD64 return (info.compRetBuffArg != BAD_VAR_NUM); #elif defined(TARGET_WINDOWS) && defined(TARGET_ARM64) - return (callConvIsInstanceMethodCallConv(compMethodInfoGetUnmanagedCallConv(info)) || compIsProfilerHookNeeded()) && + return (callConvIsInstanceMethodCallConv(compMethodInfoGetUnmanagedCallConv(info.compMethodInfo)) || compIsProfilerHookNeeded()) && (info.compRetBuffArg != BAD_VAR_NUM); #else // !TARGET_AMD64 return (compIsProfilerHookNeeded()) && (info.compRetBuffArg != BAD_VAR_NUM); From 71c903274ef5d4d89678f386c4c3fb569aa8a18f Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 2 Nov 2020 15:04:29 -0800 Subject: [PATCH 60/73] Use a union with the tailCalInfo pointer to store the unmanaged calling convention since native calls cannot be tail calls. --- src/coreclr/src/jit/gentree.cpp | 3 +-- src/coreclr/src/jit/gentree.h | 14 +++++++++++--- src/coreclr/src/jit/importer.cpp | 18 +++++++++--------- src/coreclr/src/jit/lower.cpp | 2 +- src/coreclr/src/jit/morph.cpp | 10 +++++----- 5 files changed, 27 insertions(+), 20 deletions(-) diff --git a/src/coreclr/src/jit/gentree.cpp b/src/coreclr/src/jit/gentree.cpp index 2b9b7d52bceb8c..469bee56bfeb9a 100644 --- a/src/coreclr/src/jit/gentree.cpp +++ b/src/coreclr/src/jit/gentree.cpp @@ -6252,7 +6252,6 @@ GenTreeCall* Compiler::gtNewCallNode( node->gtRetClsHnd = nullptr; node->gtControlExpr = nullptr; node->gtCallMoreFlags = 0; - node->unmgdCallConv = CORINFO_UNMANAGED_CALLCONV_MANAGED; if (callType == CT_INDIRECT) { @@ -15747,7 +15746,7 @@ GenTree* Compiler::gtNewRefCOMfield(GenTree* objPtr, #if FEATURE_MULTIREG_RET if (varTypeIsStruct(call)) { - call->InitializeStructReturnType(this, structType, call->unmgdCallConv); + call->InitializeStructReturnType(this, structType, call->GetUnmanagedCallConv()); } #endif // FEATURE_MULTIREG_RET diff --git a/src/coreclr/src/jit/gentree.h b/src/coreclr/src/jit/gentree.h index d8c02386830c6c..f6857d2d343822 100644 --- a/src/coreclr/src/jit/gentree.h +++ b/src/coreclr/src/jit/gentree.h @@ -3951,7 +3951,12 @@ struct GenTreeCall final : public GenTree CORINFO_SIG_INFO* callSig; #endif - TailCallSiteInfo* tailCallInfo; + union + { + TailCallSiteInfo* tailCallInfo; + // Only used for unmanaged calls, which cannot be tail-called + CorInfoUnmanagedCallConv unmgdCallConv; + }; #if FEATURE_MULTIREG_RET @@ -4506,8 +4511,6 @@ struct GenTreeCall final : public GenTree unsigned char gtCallType : 3; // value from the gtCallTypes enumeration unsigned char gtReturnType : 5; // exact return type - CorInfoUnmanagedCallConv unmgdCallConv; - CORINFO_CLASS_HANDLE gtRetClsHnd; // The return type handle of the call if it is a struct; always available union { @@ -4559,6 +4562,11 @@ struct GenTreeCall final : public GenTree bool AreArgsComplete() const; + CorInfoUnmanagedCallConv GetUnmanagedCallConv() const + { + return IsUnmanaged() ? unmgdCallConv : CORINFO_UNMANAGED_CALLCONV_MANAGED; + } + static bool Equals(GenTreeCall* c1, GenTreeCall* c2); GenTreeCall(var_types type) : GenTree(GT_CALL, type) diff --git a/src/coreclr/src/jit/importer.cpp b/src/coreclr/src/jit/importer.cpp index d5600d677362fa..af7480f5b71cbb 100644 --- a/src/coreclr/src/jit/importer.cpp +++ b/src/coreclr/src/jit/importer.cpp @@ -1185,7 +1185,7 @@ GenTree* Compiler::impAssignStructPtr(GenTree* destAddr, // Unmanaged instance methods on Windows need the retbuf arg after the first (this) parameter if (srcCall->IsUnmanaged()) { - if (callConvIsInstanceMethodCallConv(srcCall->unmgdCallConv)) + if (callConvIsInstanceMethodCallConv(srcCall->GetUnmanagedCallConv())) { GenTreeCall::Use* thisArg = gtInsertNewCallArgAfter(destAddr, srcCall->gtCallArgs); } @@ -8730,7 +8730,7 @@ var_types Compiler::impImportCall(OPCODE opcode, if (canTailCall && !impTailCallRetTypeCompatible(info.compRetType, info.compMethodInfo->args.retTypeClass, compMethodInfoGetUnmanagedCallConv(info.compMethodInfo), callRetTyp, - sig->retTypeClass, call->AsCall()->unmgdCallConv)) + sig->retTypeClass, call->AsCall()->GetUnmanagedCallConv())) { canTailCall = false; szCanTailCallFailReason = "Return types are not tail call compatible"; @@ -9174,7 +9174,7 @@ GenTree* Compiler::impFixupCallStructReturn(GenTreeCall* call, CORINFO_CLASS_HAN call->gtRetClsHnd = retClsHnd; #if FEATURE_MULTIREG_RET - call->InitializeStructReturnType(this, retClsHnd, call->unmgdCallConv); + call->InitializeStructReturnType(this, retClsHnd, call->GetUnmanagedCallConv()); #endif // FEATURE_MULTIREG_RET #ifdef UNIX_AMD64_ABI @@ -9231,7 +9231,7 @@ GenTree* Compiler::impFixupCallStructReturn(GenTreeCall* call, CORINFO_CLASS_HAN // No need to assign a multi-reg struct to a local var if: // - It is a tail call or // - The call is marked for in-lining later - return impAssignMultiRegTypeToVar(call, retClsHnd DEBUGARG(call->unmgdCallConv)); + return impAssignMultiRegTypeToVar(call, retClsHnd DEBUGARG(call->GetUnmanagedCallConv())); } } @@ -9244,7 +9244,7 @@ GenTree* Compiler::impFixupCallStructReturn(GenTreeCall* call, CORINFO_CLASS_HAN structPassingKind howToReturnStruct; var_types returnType; - returnType = getReturnTypeForStruct(retClsHnd, call->unmgdCallConv, &howToReturnStruct); + returnType = getReturnTypeForStruct(retClsHnd, call->GetUnmanagedCallConv(), &howToReturnStruct); if (howToReturnStruct == SPK_ByReference) { @@ -9310,7 +9310,7 @@ GenTree* Compiler::impFixupCallStructReturn(GenTreeCall* call, CORINFO_CLASS_HAN // No need to assign a multi-reg struct to a local var if: // - It is a tail call or // - The call is marked for in-lining later - return impAssignMultiRegTypeToVar(call, retClsHnd DEBUGARG(call->unmgdCallConv)); + return impAssignMultiRegTypeToVar(call, retClsHnd DEBUGARG(call->GetUnmanagedCallConv())); } } #endif // FEATURE_MULTIREG_RET @@ -13390,7 +13390,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) // Calls with large struct return value have to go through this. // Helper calls with small struct return value also have to go // through this since they do not follow Unix calling convention. - if (op1->gtOper != GT_CALL || !IsMultiRegReturnedType(clsHnd, op1->AsCall()->unmgdCallConv) || + if (op1->gtOper != GT_CALL || !IsMultiRegReturnedType(clsHnd, op1->AsCall()->GetUnmanagedCallConv()) || op1->AsCall()->gtCallType == CT_HELPER) #endif // UNIX_AMD64_ABI { @@ -15483,7 +15483,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) { op1->AsCall()->gtRetClsHnd = classHandle; #if FEATURE_MULTIREG_RET - op1->AsCall()->InitializeStructReturnType(this, classHandle, op1->AsCall()->unmgdCallConv); + op1->AsCall()->InitializeStructReturnType(this, classHandle, op1->AsCall()->GetUnmanagedCallConv()); #endif } @@ -15530,7 +15530,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) if (!compDoOldStructRetyping()) { #if FEATURE_MULTIREG_RET - op1->AsCall()->InitializeStructReturnType(this, tokenType, op1->AsCall()->unmgdCallConv); + op1->AsCall()->InitializeStructReturnType(this, tokenType, op1->AsCall()->GetUnmanagedCallConv()); #endif op1->AsCall()->gtRetClsHnd = tokenType; } diff --git a/src/coreclr/src/jit/lower.cpp b/src/coreclr/src/jit/lower.cpp index 396aadc0e15e2d..efe1db89d18706 100644 --- a/src/coreclr/src/jit/lower.cpp +++ b/src/coreclr/src/jit/lower.cpp @@ -3473,7 +3473,7 @@ void Lowering::LowerCallStruct(GenTreeCall* call) assert(!comp->compDoOldStructRetyping()); CORINFO_CLASS_HANDLE retClsHnd = call->gtRetClsHnd; Compiler::structPassingKind howToReturnStruct; - var_types returnType = comp->getReturnTypeForStruct(retClsHnd, call->unmgdCallConv, &howToReturnStruct); + var_types returnType = comp->getReturnTypeForStruct(retClsHnd, call->GetUnmanagedCallConv(), &howToReturnStruct); assert(returnType != TYP_STRUCT && returnType != TYP_UNKNOWN); var_types origType = call->TypeGet(); call->gtType = genActualType(returnType); diff --git a/src/coreclr/src/jit/morph.cpp b/src/coreclr/src/jit/morph.cpp index 63d34a5bd50335..6eaf234f2290bf 100644 --- a/src/coreclr/src/jit/morph.cpp +++ b/src/coreclr/src/jit/morph.cpp @@ -2677,7 +2677,7 @@ void Compiler::fgInitArgInfo(GenTreeCall* call) CLANG_FORMAT_COMMENT_ANCHOR; #if defined(TARGET_WINDOWS) && !defined(TARGET_ARM) - if (hasFixedRetBuffReg() && call->HasRetBufArg() && !callConvIsInstanceMethodCallConv(call->unmgdCallConv)) + if (hasFixedRetBuffReg() && call->HasRetBufArg() && !callConvIsInstanceMethodCallConv(call->GetUnmanagedCallConv())) #else if (hasFixedRetBuffReg() && call->HasRetBufArg()) #endif @@ -5105,7 +5105,7 @@ void Compiler::fgFixupStructReturn(GenTree* callNode) } else { - returnType = getReturnTypeForStruct(retClsHnd, call->unmgdCallConv, &howToReturnStruct); + returnType = getReturnTypeForStruct(retClsHnd, call->GetUnmanagedCallConv(), &howToReturnStruct); } if (howToReturnStruct == SPK_ByReference) @@ -6771,7 +6771,7 @@ bool Compiler::fgCanFastTailCall(GenTreeCall* callee, const char** failReason) assert(impTailCallRetTypeCompatible(retType, info.compMethodInfo->args.retTypeClass, compMethodInfoGetUnmanagedCallConv(info.compMethodInfo), (var_types)callee->gtReturnType, callee->gtRetClsHnd, - callee->unmgdCallConv)); + callee->GetUnmanagedCallConv())); } #endif @@ -7694,7 +7694,7 @@ GenTree* Compiler::fgMorphPotentialTailCall(GenTreeCall* call) { CORINFO_CLASS_HANDLE retClsHnd = call->gtRetClsHnd; Compiler::structPassingKind howToReturnStruct; - callType = getReturnTypeForStruct(retClsHnd, call->unmgdCallConv, &howToReturnStruct); + callType = getReturnTypeForStruct(retClsHnd, call->GetUnmanagedCallConv(), &howToReturnStruct); assert((howToReturnStruct != SPK_Unknown) && (howToReturnStruct != SPK_ByReference)); if (howToReturnStruct == SPK_ByValue) { @@ -8057,7 +8057,7 @@ GenTree* Compiler::fgCreateCallDispatcherAndGetResult(GenTreeCall* orig if (varTypeIsStruct(origCall->gtType)) { - retVal = impFixupStructReturnType(retVal, origCall->gtRetClsHnd, origCall->unmgdCallConv); + retVal = impFixupStructReturnType(retVal, origCall->gtRetClsHnd, origCall->GetUnmanagedCallConv()); } } else From b334b3721f13c8aac2cad973677490627300abd1 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 3 Nov 2020 09:59:31 -0800 Subject: [PATCH 61/73] Fix formatting. --- src/coreclr/src/jit/compiler.h | 3 ++- src/coreclr/src/jit/gentree.h | 3 +-- src/coreclr/src/jit/importer.cpp | 6 ++++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/coreclr/src/jit/compiler.h b/src/coreclr/src/jit/compiler.h index fdc7ada0ecf54d..60b392d2b2f296 100644 --- a/src/coreclr/src/jit/compiler.h +++ b/src/coreclr/src/jit/compiler.h @@ -9334,7 +9334,8 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #ifdef TARGET_AMD64 return (info.compRetBuffArg != BAD_VAR_NUM); #elif defined(TARGET_WINDOWS) && defined(TARGET_ARM64) - return (callConvIsInstanceMethodCallConv(compMethodInfoGetUnmanagedCallConv(info.compMethodInfo)) || compIsProfilerHookNeeded()) && + return (callConvIsInstanceMethodCallConv(compMethodInfoGetUnmanagedCallConv(info.compMethodInfo)) || + compIsProfilerHookNeeded()) && (info.compRetBuffArg != BAD_VAR_NUM); #else // !TARGET_AMD64 return (compIsProfilerHookNeeded()) && (info.compRetBuffArg != BAD_VAR_NUM); diff --git a/src/coreclr/src/jit/gentree.h b/src/coreclr/src/jit/gentree.h index f6857d2d343822..bc70662fc71d08 100644 --- a/src/coreclr/src/jit/gentree.h +++ b/src/coreclr/src/jit/gentree.h @@ -3951,8 +3951,7 @@ struct GenTreeCall final : public GenTree CORINFO_SIG_INFO* callSig; #endif - union - { + union { TailCallSiteInfo* tailCallInfo; // Only used for unmanaged calls, which cannot be tail-called CorInfoUnmanagedCallConv unmgdCallConv; diff --git a/src/coreclr/src/jit/importer.cpp b/src/coreclr/src/jit/importer.cpp index af7480f5b71cbb..9d6bfc9d77c281 100644 --- a/src/coreclr/src/jit/importer.cpp +++ b/src/coreclr/src/jit/importer.cpp @@ -13390,7 +13390,8 @@ void Compiler::impImportBlockCode(BasicBlock* block) // Calls with large struct return value have to go through this. // Helper calls with small struct return value also have to go // through this since they do not follow Unix calling convention. - if (op1->gtOper != GT_CALL || !IsMultiRegReturnedType(clsHnd, op1->AsCall()->GetUnmanagedCallConv()) || + if (op1->gtOper != GT_CALL || + !IsMultiRegReturnedType(clsHnd, op1->AsCall()->GetUnmanagedCallConv()) || op1->AsCall()->gtCallType == CT_HELPER) #endif // UNIX_AMD64_ABI { @@ -15483,7 +15484,8 @@ void Compiler::impImportBlockCode(BasicBlock* block) { op1->AsCall()->gtRetClsHnd = classHandle; #if FEATURE_MULTIREG_RET - op1->AsCall()->InitializeStructReturnType(this, classHandle, op1->AsCall()->GetUnmanagedCallConv()); + op1->AsCall()->InitializeStructReturnType(this, classHandle, + op1->AsCall()->GetUnmanagedCallConv()); #endif } From 28b1d484c18ddd43b8377f8b5f93ca86f0296476 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 23 Nov 2020 14:01:28 -0800 Subject: [PATCH 62/73] Update src/coreclr/src/jit/importer.cpp Co-authored-by: Andy Ayers --- src/coreclr/src/jit/importer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/src/jit/importer.cpp b/src/coreclr/src/jit/importer.cpp index 9d6bfc9d77c281..384c259a24aeb4 100644 --- a/src/coreclr/src/jit/importer.cpp +++ b/src/coreclr/src/jit/importer.cpp @@ -1205,7 +1205,7 @@ GenTree* Compiler::impAssignStructPtr(GenTree* destAddr, { for (; lastArg->GetNext() != nullptr; lastArg = lastArg->GetNext()) ; - lastArg->SetNext(gtPrependNewCallArg(destAddr, lastArg->GetNext())); + gtInsertNewCallArgAfter(destAddr, lastArg); } #else // insert the return value buffer into the argument list as first byref parameter From aab5ca05f5cee8c1629724e6e785e792af4a555e Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 23 Nov 2020 14:12:50 -0800 Subject: [PATCH 63/73] Collapse identical code paths. Clean up comments --- src/coreclr/src/jit/importer.cpp | 36 ++++---------------------------- src/coreclr/src/jit/lclvars.cpp | 7 ++----- 2 files changed, 6 insertions(+), 37 deletions(-) diff --git a/src/coreclr/src/jit/importer.cpp b/src/coreclr/src/jit/importer.cpp index 384c259a24aeb4..807042d5ac3f98 100644 --- a/src/coreclr/src/jit/importer.cpp +++ b/src/coreclr/src/jit/importer.cpp @@ -9324,7 +9324,6 @@ GenTree* Compiler::impFixupCallStructReturn(GenTreeCall* call, CORINFO_CLASS_HAN /***************************************************************************** For struct return values, re-type the operand in the case where the ABI does not use a struct return buffer - Note that this method is only call for !TARGET_X86 */ GenTree* Compiler::impFixupStructReturnType(GenTree* op, @@ -9339,9 +9338,9 @@ GenTree* Compiler::impFixupStructReturnType(GenTree* op, #if defined(TARGET_XARCH) -#ifdef UNIX_AMD64_ABI +#if defined(UNIX_AMD64_ABI) || defined(TARGET_X86) // No VarArgs for CoreCLR on x64 Unix - assert(!info.compIsVarArgs); + UNIX_AMD64_ABI_ONLY(assert(!info.compIsVarArgs)); // Is method returning a multi-reg struct? if (varTypeIsStruct(info.compRetNativeType) && IsMultiRegReturnedType(retClsHnd, unmgdCallConv)) @@ -9369,36 +9368,9 @@ GenTree* Compiler::impFixupStructReturnType(GenTree* op, return impAssignMultiRegTypeToVar(op, retClsHnd DEBUGARG(unmgdCallConv)); } -#elif defined(TARGET_X86) - // Is method returning a multi-reg struct? - if (varTypeIsStruct(info.compRetNativeType) && IsMultiRegReturnedType(retClsHnd, unmgdCallConv)) - { - // In case of multi-reg struct return, we force IR to be one of the following: - // GT_RETURN(lclvar) or GT_RETURN(call). If op is anything other than a - // lclvar or call, it is assigned to a temp to create: temp = op and GT_RETURN(tmp). - - if (op->gtOper == GT_LCL_VAR) - { - // Make sure that this struct stays in memory and doesn't get promoted. - unsigned lclNum = op->AsLclVarCommon()->GetLclNum(); - lvaTable[lclNum].lvIsMultiRegRet = true; - - // TODO-1stClassStructs: Handle constant propagation and CSE-ing of multireg returns. - op->gtFlags |= GTF_DONT_CSE; - - return op; - } - - if (op->gtOper == GT_CALL) - { - return op; - } - - return impAssignMultiRegTypeToVar(op, retClsHnd DEBUGARG(unmgdCallConv)); - } -#else // !UNIX_AMD64_ABI +#else assert(info.compRetNativeType != TYP_STRUCT); -#endif // !UNIX_AMD64_ABI +#endif // defined(UNIX_AMD64_ABI) || defined(TARGET_X86) #elif FEATURE_MULTIREG_RET && defined(TARGET_ARM) diff --git a/src/coreclr/src/jit/lclvars.cpp b/src/coreclr/src/jit/lclvars.cpp index a93d09bb299c17..cf2aa894de8a5d 100644 --- a/src/coreclr/src/jit/lclvars.cpp +++ b/src/coreclr/src/jit/lclvars.cpp @@ -577,11 +577,8 @@ void Compiler::lvaInitRetBuffArg(InitVarDscInfo* varDscInfo, bool useFixedRetBuf } //----------------------------------------------------------------------------- -// getReturnTypeForStruct: -// Get the type that is used to return values of the given struct type. -// If you have already retrieved the struct size then it should be -// passed as the optional third argument, as this allows us to avoid -// an extra call to getClassSize(clsHnd) +// lvaInitUserArgs: +// Initialize local var descriptions for incoming user arguments // // Arguments: // varDscInfo - the local var descriptions From 96081a003787594427b138477a93359182a25274 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 23 Nov 2020 17:06:01 -0800 Subject: [PATCH 64/73] Add some comments around CorInfoUnmanagedCallConv Signed-off-by: Jeremy Koritzinsky --- src/coreclr/src/inc/corinfo.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/coreclr/src/inc/corinfo.h b/src/coreclr/src/inc/corinfo.h index e63738db913364..18fcb813f6d97f 100644 --- a/src/coreclr/src/inc/corinfo.h +++ b/src/coreclr/src/inc/corinfo.h @@ -734,16 +734,21 @@ inline bool IsCallerPop(CorInfoCallConv callConv) } #endif // UNIX_X86_ABI +// Represents the calling conventions supported with the extensible calling convention syntax +// as well as the metadata-encoded calling conventions and the managed calling convention. 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, + // This represents the managed calling convention. We need some way to represent the + // managed calling convention here since we use this type within the JIT for choosing + // how to home return values and arguments. CORINFO_UNMANAGED_CALLCONV_MANAGED + // New calling conventions supported with the extensible calling convention encoding go here. }; // These are returned from getMethodOptions From 0ef494800426bf2d470b10b950512e0c266e8a84 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 24 Nov 2020 10:52:33 -0800 Subject: [PATCH 65/73] CALLCONV_MANAGED=CALLCONV_UNKNOWN jit local --- src/coreclr/src/inc/corinfo.h | 8 ++------ src/coreclr/src/jit/gentree.h | 5 +++++ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/coreclr/src/inc/corinfo.h b/src/coreclr/src/inc/corinfo.h index 18fcb813f6d97f..d0234760e55bb4 100644 --- a/src/coreclr/src/inc/corinfo.h +++ b/src/coreclr/src/inc/corinfo.h @@ -735,7 +735,7 @@ inline bool IsCallerPop(CorInfoCallConv callConv) #endif // UNIX_X86_ABI // Represents the calling conventions supported with the extensible calling convention syntax -// as well as the metadata-encoded calling conventions and the managed calling convention. +// as well as the original metadata-encoded calling conventions. enum CorInfoUnmanagedCallConv { // These correspond to CorUnmanagedCallingConvention @@ -743,11 +743,7 @@ enum CorInfoUnmanagedCallConv CORINFO_UNMANAGED_CALLCONV_C, CORINFO_UNMANAGED_CALLCONV_STDCALL, CORINFO_UNMANAGED_CALLCONV_THISCALL, - CORINFO_UNMANAGED_CALLCONV_FASTCALL, - // This represents the managed calling convention. We need some way to represent the - // managed calling convention here since we use this type within the JIT for choosing - // how to home return values and arguments. - CORINFO_UNMANAGED_CALLCONV_MANAGED + CORINFO_UNMANAGED_CALLCONV_FASTCALL // New calling conventions supported with the extensible calling convention encoding go here. }; diff --git a/src/coreclr/src/jit/gentree.h b/src/coreclr/src/jit/gentree.h index bc70662fc71d08..ca53cd2e500429 100644 --- a/src/coreclr/src/jit/gentree.h +++ b/src/coreclr/src/jit/gentree.h @@ -3817,6 +3817,11 @@ class TailCallSiteInfo class fgArgInfo; +// This represents the managed calling convention. We need some way to represent the +// managed calling convention here since we use this type within the JIT for choosing +// how to home return values and arguments. +#define CORINFO_UNMANAGED_CALLCONV_MANAGED CORINFO_UNMANAGED_CALLCONV_UNKNOWN + struct GenTreeCall final : public GenTree { class Use From 6ee4a824402b3a77c0b473d681e54aeb34d9619b Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 30 Nov 2020 12:01:11 -0800 Subject: [PATCH 66/73] PR feedback. --- src/coreclr/src/inc/corinfo.h | 6 ++++ src/coreclr/src/jit/codegenxarch.cpp | 7 ++--- src/coreclr/src/jit/compiler.cpp | 12 ++------ src/coreclr/src/jit/compiler.h | 42 ++++++++++++++-------------- src/coreclr/src/jit/gentree.h | 11 +++++++- src/coreclr/src/jit/lclvars.cpp | 3 -- 6 files changed, 42 insertions(+), 39 deletions(-) diff --git a/src/coreclr/src/inc/corinfo.h b/src/coreclr/src/inc/corinfo.h index d0234760e55bb4..130ecbe6b922cd 100644 --- a/src/coreclr/src/inc/corinfo.h +++ b/src/coreclr/src/inc/corinfo.h @@ -747,6 +747,12 @@ enum CorInfoUnmanagedCallConv // 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(CorInfoUnmanagedCallConv callConv) +{ + return callConv == CORINFO_UNMANAGED_CALLCONV_THISCALL; +} + // These are returned from getMethodOptions enum CorInfoOptions { diff --git a/src/coreclr/src/jit/codegenxarch.cpp b/src/coreclr/src/jit/codegenxarch.cpp index 1a1b92ec98283c..5aa0a93140a4c2 100644 --- a/src/coreclr/src/jit/codegenxarch.cpp +++ b/src/coreclr/src/jit/codegenxarch.cpp @@ -5727,11 +5727,10 @@ void CodeGen::genJmpMethod(GenTree* jmp) #ifdef TARGET_X86 if (varTypeIsStruct(varDsc->TypeGet())) { - assert(compiler->isTrivialPointerSizedStruct(varDsc->GetStructHnd())); + // Treat trivial pointer-sized structs as a pointer sized primitive + // for the purposes of registers. + loadType = TYP_I_IMPL; } - // Treat trivial pointer-sized structs as a pointer sized primitive - // for the purposes of registers. - loadType = TYP_I_IMPL; #endif regNumber argReg = varDsc->GetArgReg(); // incoming arg register diff --git a/src/coreclr/src/jit/compiler.cpp b/src/coreclr/src/jit/compiler.cpp index 2d1419060f9b21..32d0bac55a7c53 100644 --- a/src/coreclr/src/jit/compiler.cpp +++ b/src/coreclr/src/jit/compiler.cpp @@ -742,7 +742,7 @@ var_types Compiler::getArgTypeForStruct(CORINFO_CLASS_HANDLE clsHnd, assert(structSize != 0); // Determine if we can pass the struct as a primitive type. -// Note that on x86 we only pass specific pointer-sized structs as primitives that the VM used to unwrap. +// Note that on x86 we only pass specific pointer-sized structs that satisfy isTrivialPointerSizedStruct checks. #ifndef TARGET_X86 #ifdef UNIX_AMD64_ABI @@ -1013,10 +1013,7 @@ var_types Compiler::getReturnTypeForStruct(CORINFO_CLASS_HANDLE clsHnd, useType = TYP_UNKNOWN; } } - -#endif // UNIX_AMD64_ABI - -#ifdef UNIX_X86_ABI +#elif UNIX_X86_ABI if (callConv != CORINFO_UNMANAGED_CALLCONV_MANAGED) { canReturnInRegister = false; @@ -2060,11 +2057,6 @@ unsigned Compiler::compGetTypeSize(CorInfoType cit, CORINFO_CLASS_HANDLE clsHnd) return sigSize; } -bool Compiler::callConvIsInstanceMethodCallConv(CorInfoUnmanagedCallConv callConv) -{ - return callConv == CORINFO_UNMANAGED_CALLCONV_THISCALL; -} - CorInfoUnmanagedCallConv Compiler::compMethodInfoGetUnmanagedCallConv(CORINFO_METHOD_INFO* mthInfo) { CorInfoCallConv callConv = mthInfo->args.getCallConv(); diff --git a/src/coreclr/src/jit/compiler.h b/src/coreclr/src/jit/compiler.h index 60b392d2b2f296..31e0693fcacb59 100644 --- a/src/coreclr/src/jit/compiler.h +++ b/src/coreclr/src/jit/compiler.h @@ -9316,30 +9316,33 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX // There are cases where implicit RetBuf argument should be explicitly returned in a register. // In such cases the return type is changed to TYP_BYREF and appropriate IR is generated. // These cases are: - // 1. Profiler Leave calllback expects the address of retbuf as return value for + // 1. on x64 Windows and Unix the address of RetBuf needs to be returned by + // methods with hidden RetBufArg in RAX. In such case GT_RETURN is of TYP_BYREF, + // returning the address of RetBuf. + CLANG_FORMAT_COMMENT_ANCHOR; +#ifdef TARGET_AMD64 + return true; +#endif // TARGET_AMD64 + // 2. Profiler Leave calllback expects the address of retbuf as return value for // methods with hidden RetBuf argument. impReturnInstruction() when profiler // callbacks are needed creates GT_RETURN(TYP_BYREF, op1 = Addr of RetBuf) for // methods with hidden RetBufArg. - // - // 2. As per the System V ABI, the address of RetBuf needs to be returned by - // methods with hidden RetBufArg in RAX. In such case GT_RETURN is of TYP_BYREF, - // returning the address of RetBuf. - // - // 3. Windows x64 native calling convention also requires the address of RetBuff - // to be returned in RAX. - // 4. Windows ARM64 native instance calling convention requires the address of RetBuff + if (compIsProfilerHookNeeded()) + { + return true; + } + // 3. Windows ARM64 native instance calling convention requires the address of RetBuff // to be returned in x0. CLANG_FORMAT_COMMENT_ANCHOR; +#if defined(TARGET_WINDOWS) && defined(TARGET_ARM64) + auto callConv = compMethodInfoGetUnmanagedCallConv(info.compMethodInfo); + if (callConvIsInstanceMethodCallConv(callConv)) + { + return true; + } +#endif // TARGET_WINDOWS && TARGET_ARM64 -#ifdef TARGET_AMD64 - return (info.compRetBuffArg != BAD_VAR_NUM); -#elif defined(TARGET_WINDOWS) && defined(TARGET_ARM64) - return (callConvIsInstanceMethodCallConv(compMethodInfoGetUnmanagedCallConv(info.compMethodInfo)) || - compIsProfilerHookNeeded()) && - (info.compRetBuffArg != BAD_VAR_NUM); -#else // !TARGET_AMD64 - return (compIsProfilerHookNeeded()) && (info.compRetBuffArg != BAD_VAR_NUM); -#endif // !TARGET_AMD64 +return false; } bool compDoOldStructRetyping() @@ -9537,9 +9540,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX // size of the type these describe. unsigned compGetTypeSize(CorInfoType cit, CORINFO_CLASS_HANDLE clsHnd); - // Determines whether or not this calling convention is an instance method calling convention. - static bool callConvIsInstanceMethodCallConv(CorInfoUnmanagedCallConv callConv); - // Gets the unmanaged calling convention the method's entry point should have. // Returns CorInfoUnmanagedCallConv::CORINFO_UNMANAGED_CALLCONV_MANAGED for the managed // calling convention. diff --git a/src/coreclr/src/jit/gentree.h b/src/coreclr/src/jit/gentree.h index ca53cd2e500429..927155a969f7e6 100644 --- a/src/coreclr/src/jit/gentree.h +++ b/src/coreclr/src/jit/gentree.h @@ -4168,7 +4168,7 @@ struct GenTreeCall final : public GenTree // importer has performed tail call checks #define GTF_CALL_M_TAILCALL 0x00000002 // GT_CALL -- the call is a tailcall #define GTF_CALL_M_VARARGS 0x00000004 // GT_CALL -- the call uses varargs ABI -#define GTF_CALL_M_RETBUFFARG 0x00000008 // GT_CALL -- first parameter is the return buffer argument +#define GTF_CALL_M_RETBUFFARG 0x00000008 // GT_CALL -- call has a return buffer argument #define GTF_CALL_M_DELEGATE_INV 0x00000010 // GT_CALL -- call to Delegate.Invoke #define GTF_CALL_M_NOGCCHECK 0x00000020 // GT_CALL -- not a call for computing full interruptability and therefore no GC check is required. #define GTF_CALL_M_SPECIAL_INTRINSIC 0x00000040 // GT_CALL -- function that could be optimized as an intrinsic @@ -4283,6 +4283,15 @@ struct GenTreeCall final : public GenTree // bool TreatAsHasRetBufArg(Compiler* compiler) const; + bool HasFixedRetBufArg() const + { +#if defined(TARGET_WINDOWS) && !defined(TARGET_ARM) + return hasFixedRetBuffReg() && HasRetBufArg() && !callConvIsInstanceMethodCallConv(GetUnmanagedCallConv()); +#else + return hasFixedRetBuffReg() && HasRetBufArg(); +#endif + } + //----------------------------------------------------------------------------------------- // HasMultiRegRetVal: whether the call node returns its value in multiple return registers. // diff --git a/src/coreclr/src/jit/lclvars.cpp b/src/coreclr/src/jit/lclvars.cpp index cf2aa894de8a5d..8f89760a30ac68 100644 --- a/src/coreclr/src/jit/lclvars.cpp +++ b/src/coreclr/src/jit/lclvars.cpp @@ -2328,8 +2328,6 @@ void Compiler::StructPromotionHelper::PromoteStructVar(unsigned lclNum) #endif -#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_ARM) || defined(TARGET_X86) - // Do we have a parameter that can be enregistered? // if (varDsc->lvIsRegArg) @@ -2368,7 +2366,6 @@ void Compiler::StructPromotionHelper::PromoteStructVar(unsigned lclNum) fieldVarDsc->SetArgReg(parentArgReg); } } -#endif // defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_ARM) || defined(TARGET_X86) #ifdef FEATURE_SIMD if (varTypeIsSIMD(pFieldInfo->fldType)) From 3c89d74832a73718b37c4d9a28113d8c14138070 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 30 Nov 2020 12:03:35 -0800 Subject: [PATCH 67/73] Use new HasFixedRetBufArg method. --- src/coreclr/src/jit/morph.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/coreclr/src/jit/morph.cpp b/src/coreclr/src/jit/morph.cpp index 6eaf234f2290bf..37af665bdb1e95 100644 --- a/src/coreclr/src/jit/morph.cpp +++ b/src/coreclr/src/jit/morph.cpp @@ -2676,11 +2676,7 @@ void Compiler::fgInitArgInfo(GenTreeCall* call) // CLANG_FORMAT_COMMENT_ANCHOR; -#if defined(TARGET_WINDOWS) && !defined(TARGET_ARM) - if (hasFixedRetBuffReg() && call->HasRetBufArg() && !callConvIsInstanceMethodCallConv(call->GetUnmanagedCallConv())) -#else - if (hasFixedRetBuffReg() && call->HasRetBufArg()) -#endif + if (call->HasFixedRetBufArg()) { args = call->gtCallArgs; assert(args != nullptr); From 2ce4270b43b58aa885b3d0d48e9eb3f4273e2d5b Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 30 Nov 2020 14:26:56 -0800 Subject: [PATCH 68/73] Fix formatting. --- src/coreclr/src/jit/compiler.h | 2 +- src/coreclr/src/jit/morph.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/src/jit/compiler.h b/src/coreclr/src/jit/compiler.h index 31e0693fcacb59..4c098136dc0584 100644 --- a/src/coreclr/src/jit/compiler.h +++ b/src/coreclr/src/jit/compiler.h @@ -9342,7 +9342,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX } #endif // TARGET_WINDOWS && TARGET_ARM64 -return false; + return false; } bool compDoOldStructRetyping() diff --git a/src/coreclr/src/jit/morph.cpp b/src/coreclr/src/jit/morph.cpp index 37af665bdb1e95..eb8c5ac66cd7bb 100644 --- a/src/coreclr/src/jit/morph.cpp +++ b/src/coreclr/src/jit/morph.cpp @@ -2664,7 +2664,7 @@ void Compiler::fgInitArgInfo(GenTreeCall* call) assert(arg2 != nullptr); nonStandardArgs.Add(arg2, REG_LNGARG_HI); } -#else // !TARGET_X86 +#else // !TARGET_X86 // TODO-X86-CQ: Currently RyuJIT/x86 passes args on the stack, so this is not needed. // If/when we change that, the following code needs to be changed to correctly support the (TBD) managed calling // convention for x86/SSE. From fb00bc9167ded1a3d5422abe2d8d3ae6ccf88a24 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 30 Nov 2020 14:31:44 -0800 Subject: [PATCH 69/73] Fix compMethodReturnsRetBufAddr Signed-off-by: Jeremy Koritzinsky --- src/coreclr/src/jit/compiler.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/coreclr/src/jit/compiler.h b/src/coreclr/src/jit/compiler.h index 4c098136dc0584..bed5162f8397f5 100644 --- a/src/coreclr/src/jit/compiler.h +++ b/src/coreclr/src/jit/compiler.h @@ -9321,7 +9321,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX // returning the address of RetBuf. CLANG_FORMAT_COMMENT_ANCHOR; #ifdef TARGET_AMD64 - return true; + return (info.compRetBuffArg != BAD_VAR_NUM); #endif // TARGET_AMD64 // 2. Profiler Leave calllback expects the address of retbuf as return value for // methods with hidden RetBuf argument. impReturnInstruction() when profiler @@ -9329,7 +9329,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX // methods with hidden RetBufArg. if (compIsProfilerHookNeeded()) { - return true; + return (info.compRetBuffArg != BAD_VAR_NUM); } // 3. Windows ARM64 native instance calling convention requires the address of RetBuff // to be returned in x0. @@ -9338,7 +9338,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX auto callConv = compMethodInfoGetUnmanagedCallConv(info.compMethodInfo); if (callConvIsInstanceMethodCallConv(callConv)) { - return true; + return (info.compRetBuffArg != BAD_VAR_NUM); } #endif // TARGET_WINDOWS && TARGET_ARM64 From 8f9478a9dcbd65fe8f7ef4779bb3fd14888d5c67 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 30 Nov 2020 15:28:26 -0800 Subject: [PATCH 70/73] Create CorInfoCallConvExtension enum instead of reusing CorInfoUnmanagedCallConv --- src/coreclr/src/jit/codegencommon.cpp | 8 +++---- src/coreclr/src/jit/codegenxarch.cpp | 2 +- src/coreclr/src/jit/compiler.cpp | 21 ++++++++++------- src/coreclr/src/jit/compiler.h | 22 ++++++++--------- src/coreclr/src/jit/compiler.hpp | 6 ++--- src/coreclr/src/jit/flowgraph.cpp | 4 ++-- src/coreclr/src/jit/gcencode.cpp | 2 +- src/coreclr/src/jit/gentree.cpp | 2 +- src/coreclr/src/jit/gentree.h | 15 ++++-------- src/coreclr/src/jit/importer.cpp | 34 +++++++++++++++------------ src/coreclr/src/jit/lclvars.cpp | 6 ++--- src/coreclr/src/jit/lower.cpp | 2 +- src/coreclr/src/jit/lsrabuild.cpp | 2 +- src/coreclr/src/jit/morph.cpp | 2 +- src/coreclr/src/jit/target.h | 19 +++++++++++++++ 15 files changed, 83 insertions(+), 64 deletions(-) diff --git a/src/coreclr/src/jit/codegencommon.cpp b/src/coreclr/src/jit/codegencommon.cpp index bbefe1e8985d7a..666b90a10af2e5 100644 --- a/src/coreclr/src/jit/codegencommon.cpp +++ b/src/coreclr/src/jit/codegencommon.cpp @@ -10065,7 +10065,7 @@ void CodeGen::genVzeroupperIfNeeded(bool check256bitOnly /* = true*/) // Return Value: // true if type is returned in multiple registers, false otherwise. // -bool Compiler::IsMultiRegReturnedType(CORINFO_CLASS_HANDLE hClass, CorInfoUnmanagedCallConv callConv) +bool Compiler::IsMultiRegReturnedType(CORINFO_CLASS_HANDLE hClass, CorInfoCallConvExtension callConv) { if (hClass == NO_CLASS_HANDLE) { @@ -11529,8 +11529,8 @@ void CodeGen::genReturn(GenTree* treeNode) } else // we must have a struct return type { - CorInfoUnmanagedCallConv callConv = - compiler->compMethodInfoGetUnmanagedCallConv(compiler->info.compMethodInfo); + CorInfoCallConvExtension callConv = + compiler->compMethodInfoGetEntrypointCallConv(compiler->info.compMethodInfo); retTypeDesc.InitializeStructReturnType(compiler, compiler->info.compMethodInfo->args.retTypeClass, callConv); @@ -11659,7 +11659,7 @@ void CodeGen::genStructReturn(GenTree* treeNode) { varDsc = compiler->lvaGetDesc(actualOp1->AsLclVar()->GetLclNum()); retTypeDesc.InitializeStructReturnType(compiler, varDsc->GetStructHnd(), - compiler->compMethodInfoGetUnmanagedCallConv( + compiler->compMethodInfoGetEntrypointCallConv( compiler->info.compMethodInfo)); assert(varDsc->lvIsMultiRegRet); } diff --git a/src/coreclr/src/jit/codegenxarch.cpp b/src/coreclr/src/jit/codegenxarch.cpp index 5aa0a93140a4c2..f454aed2ccac42 100644 --- a/src/coreclr/src/jit/codegenxarch.cpp +++ b/src/coreclr/src/jit/codegenxarch.cpp @@ -141,7 +141,7 @@ void CodeGen::genEmitGSCookieCheck(bool pushReg) else // we must have a struct return type { retTypeDesc.InitializeStructReturnType(compiler, compiler->info.compMethodInfo->args.retTypeClass, - compiler->compMethodInfoGetUnmanagedCallConv( + compiler->compMethodInfoGetEntrypointCallConv( compiler->info.compMethodInfo)); } diff --git a/src/coreclr/src/jit/compiler.cpp b/src/coreclr/src/jit/compiler.cpp index 32d0bac55a7c53..d75a7a19169cf5 100644 --- a/src/coreclr/src/jit/compiler.cpp +++ b/src/coreclr/src/jit/compiler.cpp @@ -966,7 +966,7 @@ var_types Compiler::getArgTypeForStruct(CORINFO_CLASS_HANDLE clsHnd, // that multiple registers are used to return this struct. // var_types Compiler::getReturnTypeForStruct(CORINFO_CLASS_HANDLE clsHnd, - CorInfoUnmanagedCallConv callConv, + CorInfoCallConvExtension callConv, structPassingKind* wbReturnStruct /* = nullptr */, unsigned structSize /* = 0 */) { @@ -1014,14 +1014,14 @@ var_types Compiler::getReturnTypeForStruct(CORINFO_CLASS_HANDLE clsHnd, } } #elif UNIX_X86_ABI - if (callConv != CORINFO_UNMANAGED_CALLCONV_MANAGED) + if (callConv != CorInfoCallConvExtension::Managed) { canReturnInRegister = false; howToReturnStruct = SPK_ByReference; useType = TYP_UNKNOWN; } #elif defined(TARGET_WINDOWS) && !defined(TARGET_ARM) - if (callConv == CORINFO_UNMANAGED_CALLCONV_THISCALL) + if (callConvIsInstanceMethodCallConv(callConv)) { canReturnInRegister = false; howToReturnStruct = SPK_ByReference; @@ -1140,7 +1140,7 @@ var_types Compiler::getReturnTypeForStruct(CORINFO_CLASS_HANDLE clsHnd, // Only 8-byte structs are return in multiple registers. // We also only support multireg struct returns on x86 to match the native calling convention. // So return 8-byte structs only when the calling convention is a native calling convention. - if (structSize == MAX_RET_MULTIREG_BYTES && callConv != CORINFO_UNMANAGED_CALLCONV_MANAGED) + if (structSize == MAX_RET_MULTIREG_BYTES && callConv != CorInfoCallConvExtension::Managed) { // setup wbPassType and useType indicate that this is return by value in multiple registers howToReturnStruct = SPK_ByValue; @@ -2057,15 +2057,20 @@ unsigned Compiler::compGetTypeSize(CorInfoType cit, CORINFO_CLASS_HANDLE clsHnd) return sigSize; } -CorInfoUnmanagedCallConv Compiler::compMethodInfoGetUnmanagedCallConv(CORINFO_METHOD_INFO* mthInfo) +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 CORINFO_UNMANAGED_CALLCONV_MANAGED; - } - return (CorInfoUnmanagedCallConv)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 diff --git a/src/coreclr/src/jit/compiler.h b/src/coreclr/src/jit/compiler.h index bed5162f8397f5..353301fd88a59a 100644 --- a/src/coreclr/src/jit/compiler.h +++ b/src/coreclr/src/jit/compiler.h @@ -2245,7 +2245,7 @@ class Compiler #if FEATURE_MULTIREG_RET GenTree* impAssignMultiRegTypeToVar(GenTree* op, - CORINFO_CLASS_HANDLE hClass DEBUGARG(CorInfoUnmanagedCallConv callConv)); + CORINFO_CLASS_HANDLE hClass DEBUGARG(CorInfoCallConvExtension callConv)); #endif // FEATURE_MULTIREG_RET GenTree* impAssignSmallStructTypeToVar(GenTree* op, CORINFO_CLASS_HANDLE hClass); @@ -2276,7 +2276,7 @@ class Compiler var_types GetHfaType(CORINFO_CLASS_HANDLE hClass); unsigned GetHfaCount(CORINFO_CLASS_HANDLE hClass); - bool IsMultiRegReturnedType(CORINFO_CLASS_HANDLE hClass, CorInfoUnmanagedCallConv callConv); + bool IsMultiRegReturnedType(CORINFO_CLASS_HANDLE hClass, CorInfoCallConvExtension callConv); //------------------------------------------------------------------------- // The following is used for validating format of EH table @@ -3844,7 +3844,7 @@ class Compiler GenTree* impFixupStructReturnType(GenTree* op, CORINFO_CLASS_HANDLE retClsHnd, - CorInfoUnmanagedCallConv unmgdCallConv); + CorInfoCallConvExtension unmgdCallConv); #ifdef DEBUG var_types impImportJitTestLabelMark(int numArgs); @@ -4081,7 +4081,7 @@ class Compiler unsigned* typeSize, bool forReturn, bool isVarArg, - CorInfoUnmanagedCallConv callConv); + CorInfoCallConvExtension callConv); bool IsIntrinsicImplementedByUserCall(NamedIntrinsic intrinsicName); bool IsTargetIntrinsic(NamedIntrinsic intrinsicName); @@ -4388,10 +4388,10 @@ class Compiler bool impTailCallRetTypeCompatible(var_types callerRetType, CORINFO_CLASS_HANDLE callerRetTypeClass, - CorInfoUnmanagedCallConv callerCallConv, + CorInfoCallConvExtension callerCallConv, var_types calleeRetType, CORINFO_CLASS_HANDLE calleeRetTypeClass, - CorInfoUnmanagedCallConv calleeCallConv); + CorInfoCallConvExtension calleeCallConv); bool impIsTailCallILPattern( bool tailPrefixed, OPCODE curOpcode, const BYTE* codeAddrOfNextOpcode, const BYTE* codeEnd, bool isRecursive); @@ -5059,7 +5059,7 @@ class Compiler // Get the type that is used to return values of the given struct type. // If the size is unknown, pass 0 and it will be determined from 'clsHnd'. var_types getReturnTypeForStruct(CORINFO_CLASS_HANDLE clsHnd, - CorInfoUnmanagedCallConv callConv, + CorInfoCallConvExtension callConv, structPassingKind* wbPassStruct = nullptr, unsigned structSize = 0); @@ -9335,7 +9335,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX // to be returned in x0. CLANG_FORMAT_COMMENT_ANCHOR; #if defined(TARGET_WINDOWS) && defined(TARGET_ARM64) - auto callConv = compMethodInfoGetUnmanagedCallConv(info.compMethodInfo); + auto callConv = compMethodInfoGetEntrypointCallConv(info.compMethodInfo); if (callConvIsInstanceMethodCallConv(callConv)) { return (info.compRetBuffArg != BAD_VAR_NUM); @@ -9540,10 +9540,8 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX // size of the type these describe. unsigned compGetTypeSize(CorInfoType cit, CORINFO_CLASS_HANDLE clsHnd); - // Gets the unmanaged calling convention the method's entry point should have. - // Returns CorInfoUnmanagedCallConv::CORINFO_UNMANAGED_CALLCONV_MANAGED for the managed - // calling convention. - CorInfoUnmanagedCallConv compMethodInfoGetUnmanagedCallConv(CORINFO_METHOD_INFO* mthInfo); + // 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/src/jit/compiler.hpp b/src/coreclr/src/jit/compiler.hpp index 7b3f9d5aac670f..3e09b9ceeee289 100644 --- a/src/coreclr/src/jit/compiler.hpp +++ b/src/coreclr/src/jit/compiler.hpp @@ -692,16 +692,14 @@ inline bool isRegParamType(var_types type) // isVarArg - whether or not this is a vararg fixed arg or variable argument // - if so on arm64 windows getArgTypeForStruct will ignore HFA // - types -// callConv - the unmanaged calling convention of the call, -// - or CORINFO_UNMANAGED_CALLCONV_MANAGED for the -// - managed calling convention. +// callConv - the calling convention of the call // inline bool Compiler::VarTypeIsMultiByteAndCanEnreg(var_types type, CORINFO_CLASS_HANDLE typeClass, unsigned* typeSize, bool forReturn, bool isVarArg, - CorInfoUnmanagedCallConv callConv) + CorInfoCallConvExtension callConv) { bool result = false; unsigned size = 0; diff --git a/src/coreclr/src/jit/flowgraph.cpp b/src/coreclr/src/jit/flowgraph.cpp index 9c460ed2f1f8aa..6b5a5d2f2c95ba 100644 --- a/src/coreclr/src/jit/flowgraph.cpp +++ b/src/coreclr/src/jit/flowgraph.cpp @@ -22826,7 +22826,7 @@ Compiler::fgWalkResult Compiler::fgUpdateInlineReturnExpressionPlaceHolder(GenTr { structPassingKind howToReturnStruct; var_types returnType = - comp->getReturnTypeForStruct(retClsHnd, CORINFO_UNMANAGED_CALLCONV_MANAGED, &howToReturnStruct); + comp->getReturnTypeForStruct(retClsHnd, CorInfoCallConvExtension::Managed, &howToReturnStruct); GenTree* parent = data->parent; switch (howToReturnStruct) @@ -22941,7 +22941,7 @@ Compiler::fgWalkResult Compiler::fgUpdateInlineReturnExpressionPlaceHolder(GenTr noway_assert(!varTypeIsStruct(effectiveValue) || (effectiveValue->OperGet() != GT_RET_EXPR) || !comp->IsMultiRegReturnedType(effectiveValue->AsRetExpr()->gtRetClsHnd, - CORINFO_UNMANAGED_CALLCONV_MANAGED)); + CorInfoCallConvExtension::Managed)); } } diff --git a/src/coreclr/src/jit/gcencode.cpp b/src/coreclr/src/jit/gcencode.cpp index 91646386da2cf3..0147932eb17125 100644 --- a/src/coreclr/src/jit/gcencode.cpp +++ b/src/coreclr/src/jit/gcencode.cpp @@ -51,7 +51,7 @@ ReturnKind GCInfo::getReturnKind() { CORINFO_CLASS_HANDLE structType = compiler->info.compMethodInfo->args.retTypeClass; var_types retType = - compiler->getReturnTypeForStruct(structType, compiler->compMethodInfoGetUnmanagedCallConv( + compiler->getReturnTypeForStruct(structType, compiler->compMethodInfoGetEntrypointCallConv( compiler->info.compMethodInfo)); switch (retType) diff --git a/src/coreclr/src/jit/gentree.cpp b/src/coreclr/src/jit/gentree.cpp index 469bee56bfeb9a..0d65f2cd372f97 100644 --- a/src/coreclr/src/jit/gentree.cpp +++ b/src/coreclr/src/jit/gentree.cpp @@ -19245,7 +19245,7 @@ GenTree* Compiler::gtNewMustThrowException(unsigned helper, var_types type, CORI // void ReturnTypeDesc::InitializeStructReturnType(Compiler* comp, CORINFO_CLASS_HANDLE retClsHnd, - CorInfoUnmanagedCallConv callConv) + CorInfoCallConvExtension callConv) { assert(!m_inited); diff --git a/src/coreclr/src/jit/gentree.h b/src/coreclr/src/jit/gentree.h index 927155a969f7e6..2a32f3735907c7 100644 --- a/src/coreclr/src/jit/gentree.h +++ b/src/coreclr/src/jit/gentree.h @@ -3634,7 +3634,7 @@ struct ReturnTypeDesc } // Initialize the Return Type Descriptor for a method that returns a struct type - void InitializeStructReturnType(Compiler* comp, CORINFO_CLASS_HANDLE retClsHnd, CorInfoUnmanagedCallConv callConv); + void InitializeStructReturnType(Compiler* comp, CORINFO_CLASS_HANDLE retClsHnd, CorInfoCallConvExtension callConv); // Initialize the Return Type Descriptor for a method that returns a TYP_LONG // Only needed for X86 and arm32. @@ -3817,11 +3817,6 @@ class TailCallSiteInfo class fgArgInfo; -// This represents the managed calling convention. We need some way to represent the -// managed calling convention here since we use this type within the JIT for choosing -// how to home return values and arguments. -#define CORINFO_UNMANAGED_CALLCONV_MANAGED CORINFO_UNMANAGED_CALLCONV_UNKNOWN - struct GenTreeCall final : public GenTree { class Use @@ -3959,7 +3954,7 @@ struct GenTreeCall final : public GenTree union { TailCallSiteInfo* tailCallInfo; // Only used for unmanaged calls, which cannot be tail-called - CorInfoUnmanagedCallConv unmgdCallConv; + CorInfoCallConvExtension unmgdCallConv; }; #if FEATURE_MULTIREG_RET @@ -4003,7 +3998,7 @@ struct GenTreeCall final : public GenTree #endif } - void InitializeStructReturnType(Compiler* comp, CORINFO_CLASS_HANDLE retClsHnd, CorInfoUnmanagedCallConv callConv) + void InitializeStructReturnType(Compiler* comp, CORINFO_CLASS_HANDLE retClsHnd, CorInfoCallConvExtension callConv) { #if FEATURE_MULTIREG_RET gtReturnTypeDesc.InitializeStructReturnType(comp, retClsHnd, callConv); @@ -4575,9 +4570,9 @@ struct GenTreeCall final : public GenTree bool AreArgsComplete() const; - CorInfoUnmanagedCallConv GetUnmanagedCallConv() const + CorInfoCallConvExtension GetUnmanagedCallConv() const { - return IsUnmanaged() ? unmgdCallConv : CORINFO_UNMANAGED_CALLCONV_MANAGED; + return IsUnmanaged() ? unmgdCallConv : CorInfoCallConvExtension::Managed; } static bool Equals(GenTreeCall* c1, GenTreeCall* c2); diff --git a/src/coreclr/src/jit/importer.cpp b/src/coreclr/src/jit/importer.cpp index 807042d5ac3f98..4d7e11e6acf7dd 100644 --- a/src/coreclr/src/jit/importer.cpp +++ b/src/coreclr/src/jit/importer.cpp @@ -6939,9 +6939,13 @@ 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++; @@ -7489,10 +7493,10 @@ void Compiler::impInsertHelperCall(CORINFO_HELPER_DESC* helperInfo) // sizes are equal and get returned in the same return register. bool Compiler::impTailCallRetTypeCompatible(var_types callerRetType, CORINFO_CLASS_HANDLE callerRetTypeClass, - CorInfoUnmanagedCallConv callerCallConv, + CorInfoCallConvExtension callerCallConv, var_types calleeRetType, CORINFO_CLASS_HANDLE calleeRetTypeClass, - CorInfoUnmanagedCallConv calleeCallConv) + CorInfoCallConvExtension calleeCallConv) { // Note that we can not relax this condition with genActualType() as the // calling convention dictates that the caller of a function with a small @@ -8729,7 +8733,7 @@ var_types Compiler::impImportCall(OPCODE opcode, if (canTailCall && !impTailCallRetTypeCompatible(info.compRetType, info.compMethodInfo->args.retTypeClass, - compMethodInfoGetUnmanagedCallConv(info.compMethodInfo), callRetTyp, + compMethodInfoGetEntrypointCallConv(info.compMethodInfo), callRetTyp, sig->retTypeClass, call->AsCall()->GetUnmanagedCallConv())) { canTailCall = false; @@ -9083,7 +9087,7 @@ bool Compiler::impMethodInfo_hasRetBuffArg(CORINFO_METHOD_INFO* methInfo) structPassingKind howToReturnStruct = SPK_Unknown; var_types returnType = getReturnTypeForStruct(methInfo->args.retTypeClass, - compMethodInfoGetUnmanagedCallConv(methInfo), &howToReturnStruct); + compMethodInfoGetEntrypointCallConv(methInfo), &howToReturnStruct); if (howToReturnStruct == SPK_ByReference) { @@ -9328,7 +9332,7 @@ GenTree* Compiler::impFixupCallStructReturn(GenTreeCall* call, CORINFO_CLASS_HAN GenTree* Compiler::impFixupStructReturnType(GenTree* op, CORINFO_CLASS_HANDLE retClsHnd, - CorInfoUnmanagedCallConv unmgdCallConv) + CorInfoCallConvExtension unmgdCallConv) { assert(varTypeIsStruct(info.compRetType)); assert(info.compRetBuffArg == BAD_VAR_NUM); @@ -15759,7 +15763,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) #if FEATURE_MULTIREG_RET if (varTypeIsStruct(op1) && - IsMultiRegReturnedType(resolvedToken.hClass, CORINFO_UNMANAGED_CALLCONV_MANAGED)) + IsMultiRegReturnedType(resolvedToken.hClass, CorInfoCallConvExtension::Managed)) { // Unbox nullable helper returns a TYP_STRUCT. // For the multi-reg case we need to spill it to a temp so that @@ -16636,7 +16640,7 @@ GenTree* Compiler::impAssignSmallStructTypeToVar(GenTree* op, CORINFO_CLASS_HAND // Tree with reference to struct local to use as call return value. GenTree* Compiler::impAssignMultiRegTypeToVar(GenTree* op, - CORINFO_CLASS_HANDLE hClass DEBUGARG(CorInfoUnmanagedCallConv callConv)) + CORINFO_CLASS_HANDLE hClass DEBUGARG(CorInfoCallConvExtension callConv)) { unsigned tmpNum = lvaGrabTemp(true DEBUGARG("Return value temp for multireg return")); impAssignTempGen(tmpNum, op, hClass, (unsigned)CHECK_SPILL_ALL); @@ -16800,7 +16804,7 @@ bool Compiler::impReturnInstruction(int prefixFlags, OPCODE& opcode) // adjust the type away from struct to integral // and no normalizing op2 = impFixupStructReturnType(op2, retClsHnd, - compMethodInfoGetUnmanagedCallConv(info.compMethodInfo)); + compMethodInfoGetEntrypointCallConv(info.compMethodInfo)); } else { @@ -17001,7 +17005,7 @@ bool Compiler::impReturnInstruction(int prefixFlags, OPCODE& opcode) #else // defined(UNIX_AMD64_ABI) ReturnTypeDesc retTypeDesc; retTypeDesc.InitializeStructReturnType(this, retClsHnd, - compMethodInfoGetUnmanagedCallConv(info.compMethodInfo)); + compMethodInfoGetEntrypointCallConv(info.compMethodInfo)); unsigned retRegCount = retTypeDesc.GetReturnRegCount(); if (retRegCount != 0) @@ -17036,7 +17040,7 @@ bool Compiler::impReturnInstruction(int prefixFlags, OPCODE& opcode) #elif defined(TARGET_ARM64) ReturnTypeDesc retTypeDesc; retTypeDesc.InitializeStructReturnType(this, retClsHnd, - compMethodInfoGetUnmanagedCallConv(info.compMethodInfo)); + compMethodInfoGetEntrypointCallConv(info.compMethodInfo)); unsigned retRegCount = retTypeDesc.GetReturnRegCount(); if (retRegCount != 0) @@ -17061,7 +17065,7 @@ bool Compiler::impReturnInstruction(int prefixFlags, OPCODE& opcode) #elif defined(TARGET_X86) ReturnTypeDesc retTypeDesc; retTypeDesc.InitializeStructReturnType(this, retClsHnd, - compMethodInfoGetUnmanagedCallConv(info.compMethodInfo)); + compMethodInfoGetEntrypointCallConv(info.compMethodInfo)); unsigned retRegCount = retTypeDesc.GetReturnRegCount(); if (retRegCount != 0) @@ -17150,7 +17154,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(compMethodInfoGetUnmanagedCallConv(info.compMethodInfo))) + else if (callConvIsInstanceMethodCallConv(compMethodInfoGetEntrypointCallConv(info.compMethodInfo))) { op1 = gtNewOperNode(GT_RETURN, TYP_BYREF, gtNewLclvNode(info.compRetBuffArg, TYP_BYREF)); } @@ -17169,7 +17173,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, compMethodInfoGetUnmanagedCallConv(info.compMethodInfo)); + op2 = impFixupStructReturnType(op2, retClsHnd, compMethodInfoGetEntrypointCallConv(info.compMethodInfo)); // return op2 var_types returnType; if (compDoOldStructRetyping()) diff --git a/src/coreclr/src/jit/lclvars.cpp b/src/coreclr/src/jit/lclvars.cpp index 8f89760a30ac68..672ca4fd117527 100644 --- a/src/coreclr/src/jit/lclvars.cpp +++ b/src/coreclr/src/jit/lclvars.cpp @@ -146,7 +146,7 @@ void Compiler::lvaInitTypeRef() Compiler::structPassingKind howToReturnStruct; var_types returnType = - getReturnTypeForStruct(retClsHnd, compMethodInfoGetUnmanagedCallConv(info.compMethodInfo), + getReturnTypeForStruct(retClsHnd, compMethodInfoGetEntrypointCallConv(info.compMethodInfo), &howToReturnStruct); // We can safely widen the return type for enclosed structs. @@ -353,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(compMethodInfoGetUnmanagedCallConv(info.compMethodInfo))) + 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 @@ -5344,7 +5344,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(compMethodInfoGetUnmanagedCallConv(info.compMethodInfo))) + if (callConvIsInstanceMethodCallConv(compMethodInfoGetEntrypointCallConv(info.compMethodInfo))) { noway_assert(lvaTable[lclNum].lvIsRegArg); #ifndef TARGET_X86 diff --git a/src/coreclr/src/jit/lower.cpp b/src/coreclr/src/jit/lower.cpp index efe1db89d18706..5be01223ab407c 100644 --- a/src/coreclr/src/jit/lower.cpp +++ b/src/coreclr/src/jit/lower.cpp @@ -3012,7 +3012,7 @@ void Lowering::LowerRet(GenTreeUnOp* ret) LclVarDsc* varDsc = nullptr; varDsc = comp->lvaGetDesc(retVal->AsLclVar()->GetLclNum()); retTypeDesc.InitializeStructReturnType(comp, varDsc->GetStructHnd(), - comp->compMethodInfoGetUnmanagedCallConv(comp->info.compMethodInfo)); + comp->compMethodInfoGetEntrypointCallConv(comp->info.compMethodInfo)); if (retTypeDesc.GetReturnRegCount() > 1) { CheckMultiRegLclVar(retVal->AsLclVar(), &retTypeDesc); diff --git a/src/coreclr/src/jit/lsrabuild.cpp b/src/coreclr/src/jit/lsrabuild.cpp index 801db356fa1826..5158e99b33038b 100644 --- a/src/coreclr/src/jit/lsrabuild.cpp +++ b/src/coreclr/src/jit/lsrabuild.cpp @@ -3485,7 +3485,7 @@ int LinearScan::BuildReturn(GenTree* tree) LclVarDsc* varDsc = compiler->lvaGetDesc(op1->AsLclVar()->GetLclNum()); ReturnTypeDesc retTypeDesc; retTypeDesc.InitializeStructReturnType(compiler, varDsc->GetStructHnd(), - compiler->compMethodInfoGetUnmanagedCallConv( + compiler->compMethodInfoGetEntrypointCallConv( compiler->info.compMethodInfo)); pRetTypeDesc = &retTypeDesc; assert(compiler->lvaGetDesc(op1->AsLclVar()->GetLclNum())->lvFieldCnt == diff --git a/src/coreclr/src/jit/morph.cpp b/src/coreclr/src/jit/morph.cpp index eb8c5ac66cd7bb..59588b3beadcb0 100644 --- a/src/coreclr/src/jit/morph.cpp +++ b/src/coreclr/src/jit/morph.cpp @@ -6765,7 +6765,7 @@ bool Compiler::fgCanFastTailCall(GenTreeCall* callee, const char** failReason) { var_types retType = (compDoOldStructRetyping() ? info.compRetNativeType : info.compRetType); assert(impTailCallRetTypeCompatible(retType, info.compMethodInfo->args.retTypeClass, - compMethodInfoGetUnmanagedCallConv(info.compMethodInfo), + compMethodInfoGetEntrypointCallConv(info.compMethodInfo), (var_types)callee->gtReturnType, callee->gtRetClsHnd, callee->GetUnmanagedCallConv())); } diff --git a/src/coreclr/src/jit/target.h b/src/coreclr/src/jit/target.h index 41d1afea124150..13f8856556653f 100644 --- a/src/coreclr/src/jit/target.h +++ b/src/coreclr/src/jit/target.h @@ -2026,6 +2026,25 @@ 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_ /*****************************************************************************/ From e26c36d8cd907753bf905689e08d7d02e42cddc7 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 30 Nov 2020 17:04:15 -0800 Subject: [PATCH 71/73] Fix the unreachable code warnings. --- src/coreclr/src/jit/compiler.h | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/coreclr/src/jit/compiler.h b/src/coreclr/src/jit/compiler.h index ef952528518372..1bf93fbce84681 100644 --- a/src/coreclr/src/jit/compiler.h +++ b/src/coreclr/src/jit/compiler.h @@ -9350,14 +9350,15 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX // There are cases where implicit RetBuf argument should be explicitly returned in a register. // In such cases the return type is changed to TYP_BYREF and appropriate IR is generated. // These cases are: + CLANG_FORMAT_COMMENT_ANCHOR; +#ifdef TARGET_AMD64 // 1. on x64 Windows and Unix the address of RetBuf needs to be returned by // methods with hidden RetBufArg in RAX. In such case GT_RETURN is of TYP_BYREF, // returning the address of RetBuf. - CLANG_FORMAT_COMMENT_ANCHOR; -#ifdef TARGET_AMD64 return (info.compRetBuffArg != BAD_VAR_NUM); -#endif // TARGET_AMD64 - // 2. Profiler Leave calllback expects the address of retbuf as return value for +#else // TARGET_AMD64 +#ifdef PROFILING_SUPPORTED + // 2. Profiler Leave callback expects the address of retbuf as return value for // methods with hidden RetBuf argument. impReturnInstruction() when profiler // callbacks are needed creates GT_RETURN(TYP_BYREF, op1 = Addr of RetBuf) for // methods with hidden RetBufArg. @@ -9365,6 +9366,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX { return (info.compRetBuffArg != BAD_VAR_NUM); } +#endif // 3. Windows ARM64 native instance calling convention requires the address of RetBuff // to be returned in x0. CLANG_FORMAT_COMMENT_ANCHOR; @@ -9377,6 +9379,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #endif // TARGET_WINDOWS && TARGET_ARM64 return false; +#endif // TARGET_AMD64 } bool compDoOldStructRetyping() From c1040aa64ba245418640bdada8e0a5577d2854cd Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 1 Dec 2020 10:31:32 -0800 Subject: [PATCH 72/73] Fix formatting --- src/coreclr/src/jit/compiler.cpp | 4 ++-- src/coreclr/src/jit/importer.cpp | 9 +++++---- src/coreclr/src/jit/lower.cpp | 3 ++- src/coreclr/src/jit/target.h | 1 - 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/coreclr/src/jit/compiler.cpp b/src/coreclr/src/jit/compiler.cpp index 4c17bed06025fc..1b765132114840 100644 --- a/src/coreclr/src/jit/compiler.cpp +++ b/src/coreclr/src/jit/compiler.cpp @@ -2063,11 +2063,11 @@ CorInfoCallConvExtension Compiler::compMethodInfoGetEntrypointCallConv(CORINFO_M // 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; } diff --git a/src/coreclr/src/jit/importer.cpp b/src/coreclr/src/jit/importer.cpp index 34db99bd1b6373..0a80dc44af9f99 100644 --- a/src/coreclr/src/jit/importer.cpp +++ b/src/coreclr/src/jit/importer.cpp @@ -7120,11 +7120,11 @@ 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 = CorInfoCallConvExtension(unmanagedCallConv); if (!call->IsSuppressGCTransition()) @@ -9267,8 +9267,9 @@ bool Compiler::impMethodInfo_hasRetBuffArg(CORINFO_METHOD_INFO* methInfo) // We have some kind of STRUCT being returned structPassingKind howToReturnStruct = SPK_Unknown; - var_types returnType = getReturnTypeForStruct(methInfo->args.retTypeClass, - compMethodInfoGetEntrypointCallConv(methInfo), &howToReturnStruct); + var_types returnType = + getReturnTypeForStruct(methInfo->args.retTypeClass, compMethodInfoGetEntrypointCallConv(methInfo), + &howToReturnStruct); if (howToReturnStruct == SPK_ByReference) { diff --git a/src/coreclr/src/jit/lower.cpp b/src/coreclr/src/jit/lower.cpp index b6540e73c7648d..176eac0d15a990 100644 --- a/src/coreclr/src/jit/lower.cpp +++ b/src/coreclr/src/jit/lower.cpp @@ -3017,7 +3017,8 @@ void Lowering::LowerRet(GenTreeUnOp* ret) LclVarDsc* varDsc = nullptr; varDsc = comp->lvaGetDesc(retVal->AsLclVar()->GetLclNum()); retTypeDesc.InitializeStructReturnType(comp, varDsc->GetStructHnd(), - comp->compMethodInfoGetEntrypointCallConv(comp->info.compMethodInfo)); + comp->compMethodInfoGetEntrypointCallConv( + comp->info.compMethodInfo)); if (retTypeDesc.GetReturnRegCount() > 1) { CheckMultiRegLclVar(retVal->AsLclVar(), &retTypeDesc); diff --git a/src/coreclr/src/jit/target.h b/src/coreclr/src/jit/target.h index 13f8856556653f..3e133f0eba32eb 100644 --- a/src/coreclr/src/jit/target.h +++ b/src/coreclr/src/jit/target.h @@ -2026,7 +2026,6 @@ 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 From fa2f2aaec968d2fbabc5b15aadef443fa89021ca Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 2 Dec 2020 10:47:16 -0800 Subject: [PATCH 73/73] Use FEATURE_MULTIREG_RET instead of TARGET_* for XARCH case. --- src/coreclr/src/jit/importer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/src/jit/importer.cpp b/src/coreclr/src/jit/importer.cpp index 0a80dc44af9f99..88e60351aaae62 100644 --- a/src/coreclr/src/jit/importer.cpp +++ b/src/coreclr/src/jit/importer.cpp @@ -9524,7 +9524,7 @@ GenTree* Compiler::impFixupStructReturnType(GenTree* op, #if defined(TARGET_XARCH) -#if defined(UNIX_AMD64_ABI) || defined(TARGET_X86) +#if FEATURE_MULTIREG_RET // No VarArgs for CoreCLR on x64 Unix UNIX_AMD64_ABI_ONLY(assert(!info.compIsVarArgs));