From 0b13768d257e44161eb767e5f6da44e4cb516ca2 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Tue, 16 Dec 2025 15:57:46 -0800 Subject: [PATCH 1/7] Add support for unmanaged thiscall calling convention - Notably, on Windows platforms, the thiscall calling convention changes the argument order of the return buffer, and changes the rules where the return buffer pointer is placed - This should also be good prep for the Swift calling convention work, which also needs to identify the swift calling convention at this point - Also a drive by fix for the behavior of explicit this managed function pointers which return values in the return buffer, we were also missing a test cases for that scenario. --- src/coreclr/vm/callconvbuilder.cpp | 20 ++ src/coreclr/vm/callconvbuilder.hpp | 16 ++ src/coreclr/vm/callstubgenerator.cpp | 176 +++++++++++++++++- src/coreclr/vm/callstubgenerator.h | 2 +- src/coreclr/vm/siginfo.cpp | 25 +++ src/coreclr/vm/siginfo.hpp | 2 + .../JitBlue/GitHub_35384/GitHub_35384.il | 133 ++++++++++++- 7 files changed, 365 insertions(+), 9 deletions(-) diff --git a/src/coreclr/vm/callconvbuilder.cpp b/src/coreclr/vm/callconvbuilder.cpp index 9e1d760dc2220b..e64ed7184bfb6b 100644 --- a/src/coreclr/vm/callconvbuilder.cpp +++ b/src/coreclr/vm/callconvbuilder.cpp @@ -354,8 +354,28 @@ HRESULT CallConv::TryGetUnmanagedCallingConventionFromModOpt( } IfFailRet(sigPtr.GetData(NULL)); // arg count +#ifdef DEBUG PCCOR_SIGNATURE pWalk = sigPtr.GetPtr(); _ASSERTE(pWalk <= pSig + cSig); +#endif + + return TryGetUnmanagedCallingConventionFromModOptSigStartingAtRetType( + pModule, + sigPtr, + builder, + errorResID); +} + +HRESULT CallConv::TryGetUnmanagedCallingConventionFromModOptSigStartingAtRetType( + _In_ CORINFO_MODULE_HANDLE pModule, + _In_ SigPointer sig, + _Inout_ CallConvBuilder* builder, + _Out_ UINT* errorResID) +{ + PCCOR_SIGNATURE pSig; + uint32_t cSig; + sig.GetSignature(&pSig, &cSig); + PCCOR_SIGNATURE pWalk = pSig; CallConvBuilder& callConvBuilder = *builder; while ((pWalk < (pSig + cSig)) && ((*pWalk == ELEMENT_TYPE_CMOD_OPT) || (*pWalk == ELEMENT_TYPE_CMOD_REQD) || (*pWalk == ELEMENT_TYPE_CMOD_INTERNAL))) diff --git a/src/coreclr/vm/callconvbuilder.hpp b/src/coreclr/vm/callconvbuilder.hpp index a2ecd43fc2581b..0d8605df1fc027 100644 --- a/src/coreclr/vm/callconvbuilder.hpp +++ b/src/coreclr/vm/callconvbuilder.hpp @@ -80,6 +80,22 @@ namespace CallConv _Inout_ CallConvBuilder *builder, _Out_ UINT* errorResID); + //------------------------------------------------------------------------- + // Gets the unmanaged calling convention by reading any modopts, starting the sig + // walk at the return type in the signature + // + // Returns: + // S_OK - No errors + // COR_E_BADIMAGEFORMAT - Signature had an invalid format + // COR_E_INVALIDPROGRAM - Program is considered invalid (more + // than one calling convention specified) + //------------------------------------------------------------------------- + HRESULT TryGetUnmanagedCallingConventionFromModOptSigStartingAtRetType( + _In_ CORINFO_MODULE_HANDLE pModule, + _In_ SigPointer sig, + _Inout_ CallConvBuilder* builder, + _Out_ UINT* errorResID); + //------------------------------------------------------------------------- // Gets the calling convention from the UnmanagedCallConv attribute // diff --git a/src/coreclr/vm/callstubgenerator.cpp b/src/coreclr/vm/callstubgenerator.cpp index a0a5f345a58979..89b2ef42adfa57 100644 --- a/src/coreclr/vm/callstubgenerator.cpp +++ b/src/coreclr/vm/callstubgenerator.cpp @@ -4,7 +4,9 @@ #if defined(FEATURE_INTERPRETER) && !defined(TARGET_WASM) #include "callstubgenerator.h" +#include "callconvbuilder.hpp" #include "ecall.h" +#include "dllimport.h" extern "C" void InjectInterpStackAlign(); extern "C" void Load_Stack(); @@ -2267,7 +2269,7 @@ CallStubHeader *CallStubGenerator::GenerateCallStub(MethodDesc *pMD, AllocMemTra PCODE *pRoutines = (PCODE*)alloca(tempStorageSize); memset(pRoutines, 0, tempStorageSize); - ComputeCallStub(sig, pRoutines); + ComputeCallStub(sig, pRoutines, pMD); LoaderAllocator *pLoaderAllocator = pMD->GetLoaderAllocator(); S_SIZE_T finalStubSize(sizeof(CallStubHeader) + m_routineIndex * sizeof(PCODE)); @@ -2364,7 +2366,7 @@ CallStubHeader *CallStubGenerator::GenerateCallStubForSig(MetaSig &sig) m_interpreterToNative = true; // We always generate the interpreter to native call stub here - ComputeCallStub(sig, pRoutines); + ComputeCallStub(sig, pRoutines, NULL); xxHash hashState; for (int i = 0; i < m_routineIndex; i++) @@ -2451,8 +2453,176 @@ void CallStubGenerator::TerminateCurrentRoutineIfNotOfNewType(RoutineType type, return; } -void CallStubGenerator::ComputeCallStub(MetaSig &sig, PCODE *pRoutines) +//--------------------------------------------------------------------------- +// isNativePrimitiveStructType: +// Check if the given struct type is an intrinsic type that should be treated as though +// it is not a struct at the unmanaged ABI boundary. +// +// Arguments: +// clsHnd - the handle for the struct type. +// +// Return Value: +// true if the given struct type should be treated as a primitive for unmanaged calls, +// false otherwise. +// +bool isNativePrimitiveStructType(MethodTable* pMT) { + if (!pMT->IsIntrinsicType()) + { + return false; + } + const char* namespaceName = nullptr; + const char* typeName = pMT->GetFullyQualifiedNameInfo(&namespaceName); + + if ((namespaceName == NULL) || (typeName == NULL)) + { + return false; + } + + if (strcmp(namespaceName, "System.Runtime.InteropServices") != 0) + { + return false; + } + + return strcmp(typeName, "CLong") == 0 || strcmp(typeName, "CULong") == 0 || strcmp(typeName, "NFloat") == 0; +} + +void CallStubGenerator::ComputeCallStub(MetaSig &sig, PCODE *pRoutines, MethodDesc *pMD) +{ + bool rewriteMetaSigFromExplicitThisToHasThis = false; + bool unmanagedThisCallConv = false; + + bool hasUnmanagedCallConv = false; + CorInfoCallConvExtension unmanagedCallConv = CorInfoCallConvExtension::C; + + if (pMD != NULL && (pMD->IsPInvoke())) + { + PInvoke::GetCallingConvention_IgnoreErrors(pMD, &unmanagedCallConv, NULL); + hasUnmanagedCallConv = true; + } + else if (pMD != NULL && pMD->HasUnmanagedCallersOnlyAttribute()) + { + if (CallConv::TryGetCallingConventionFromUnmanagedCallersOnly(pMD, &unmanagedCallConv)) + { + if (sig.GetCallingConvention() == IMAGE_CEE_CS_CALLCONV_VARARG) + { + unmanagedCallConv = CorInfoCallConvExtension::C; + } + unmanagedCallConv = unmanagedCallConv; + } + else + { + unmanagedCallConv = CallConv::GetDefaultUnmanagedCallingConvention(); + } + hasUnmanagedCallConv = true; + } + else + { + switch (sig.GetCallingConvention()) + { + case IMAGE_CEE_CS_CALLCONV_THISCALL: + unmanagedCallConv = CorInfoCallConvExtension::Thiscall; + hasUnmanagedCallConv = true; + break; + case IMAGE_CEE_CS_CALLCONV_UNMANAGED: + unmanagedCallConv = GetUnmanagedCallConvExtension(&sig); + hasUnmanagedCallConv = true; + break; + } + } + + if (hasUnmanagedCallConv) + { + switch (unmanagedCallConv) + { + case CorInfoCallConvExtension::Thiscall: + case CorInfoCallConvExtension::CMemberFunction: + case CorInfoCallConvExtension::StdcallMemberFunction: + case CorInfoCallConvExtension::FastcallMemberFunction: + unmanagedThisCallConv = true; + break; + default: + break; + } + } + +#if defined(TARGET_WINDOWS) + // On these platforms, when making a ThisCall, or other call using a C++ MemberFunction calling convention, + // the "this" pointer is passed in the first argument slot. + bool rewriteReturnTypeToForceRetBuf = false; + if (unmanagedThisCallConv) + { + rewriteMetaSigFromExplicitThisToHasThis = true; + // Also, any struct type other than a few special cases is returned via return buffer for unmanaged calls + CorElementType retType = sig.GetReturnType(); + sig.Reset(); + + if (retType == ELEMENT_TYPE_VALUETYPE) + { + TypeHandle thRetType = sig.GetRetTypeHandleThrowing(); + MethodTable* pMTRetType = thRetType.AsMethodTable(); + + if (pMTRetType->GetInternalCorElementType() == ELEMENT_TYPE_VALUETYPE && !isNativePrimitiveStructType(pMTRetType)) + { + rewriteReturnTypeToForceRetBuf = true; + } + } + } +#endif // defined(TARGET_WINDOWS) + + // Rewrite ExplicitThis to HasThis. This allows us to use ArgIterator which is unaware of ExplicitThis + // in the places where it is needed such as computation of return buffers. + if (sig.GetCallingConventionInfo() & IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS) + { + printf("Managed ExplicitThis to HasThis conversion needed\n"); + rewriteMetaSigFromExplicitThisToHasThis = true; + } + + SigBuilder sigBuilder; + if (rewriteMetaSigFromExplicitThisToHasThis) + { + printf("Rewriting ExplicitThis to implicit this\n"); + sigBuilder.AppendByte(IMAGE_CEE_CS_CALLCONV_DEFAULT_HASTHIS); + if ((sig.NumFixedArgs() == 0) || (sig.HasThis() && !sig.HasExplicitThis())) + { + ThrowHR(COR_E_BADIMAGEFORMAT); + } + sigBuilder.AppendData(sig.NumFixedArgs() - 1); + TypeHandle thRetType = sig.GetRetTypeHandleThrowing(); +#if defined(TARGET_WINDOWS) + if (rewriteReturnTypeToForceRetBuf) + { + // Change the return type to type large enough it will always need to be returned via return buffer + thRetType = CoreLibBinder::GetClass(CLASS__STACKFRAMEITERATOR); + _ASSERTE(thRetType.IsValueType()); + _ASSERTE(thRetType.GetSize() > 64); + sigBuilder.AppendElementType(ELEMENT_TYPE_INTERNAL); + sigBuilder.AppendPointer(thRetType.AsPtr()); + } + else +#endif + { + SigPointer pReturn = sig.GetReturnProps(); + pReturn.ConvertToInternalExactlyOne(sig.GetModule(), sig.GetSigTypeContext(), &sigBuilder); + } + + // Skip the explicit this argument + sig.NextArg(); + + // Copy rest of the arguments + sig.NextArg(); + SigPointer pArgs = sig.GetArgProps(); + for (unsigned i = 1; i < sig.NumFixedArgs(); i++) + { + pArgs.ConvertToInternalExactlyOne(sig.GetModule(), sig.GetSigTypeContext(), &sigBuilder); + } + + DWORD cSig; + PCCOR_SIGNATURE pNewSig = (PCCOR_SIGNATURE)sigBuilder.GetSignature(&cSig); + MetaSig newSig(pNewSig, cSig, sig.GetModule(), NULL, MetaSig::sigMember); + sig = newSig; + } + ArgIterator argIt(&sig); int32_t interpreterStackOffset = 0; diff --git a/src/coreclr/vm/callstubgenerator.h b/src/coreclr/vm/callstubgenerator.h index 47cc34a14c3cac..292801d6c62fe5 100644 --- a/src/coreclr/vm/callstubgenerator.h +++ b/src/coreclr/vm/callstubgenerator.h @@ -179,7 +179,7 @@ class CallStubGenerator // The size of the routines array is three times the number of arguments plus one slot for the target method pointer. return sizeof(CallStubHeader) + ((numArgs + 1) * 3 + 1) * sizeof(PCODE); } - void ComputeCallStub(MetaSig &sig, PCODE *pRoutines); + void ComputeCallStub(MetaSig &sig, PCODE *pRoutines, MethodDesc *pMD); void TerminateCurrentRoutineIfNotOfNewType(RoutineType type, PCODE *pRoutines); }; diff --git a/src/coreclr/vm/siginfo.cpp b/src/coreclr/vm/siginfo.cpp index 023b85030f0e0a..b3dcfaed54168e 100644 --- a/src/coreclr/vm/siginfo.cpp +++ b/src/coreclr/vm/siginfo.cpp @@ -23,6 +23,8 @@ #include #include "argdestination.h" #include "multicorejit.h" +#include "callconvbuilder.hpp" +#include "dynamicmethod.h" /*******************************************************************/ const CorTypeInfo::CorTypeInfoEntry CorTypeInfo::info[ELEMENT_TYPE_MAX] = @@ -5903,3 +5905,26 @@ BOOL CompareTypeLayout(mdToken tk1, mdToken tk2, Module *pModule1, Module *pModu return TRUE; } + +#ifndef DACCESS_COMPILE +CorInfoCallConvExtension GetUnmanagedCallConvExtension(MetaSig* pSig) +{ + STANDARD_VM_CONTRACT; + CallConvBuilder builder; + UINT errorResID; + + HRESULT hr = CallConv::TryGetUnmanagedCallingConventionFromModOptSigStartingAtRetType(GetScopeHandle(pSig->GetModule()), pSig->GetReturnProps(), &builder, &errorResID); + + if (FAILED(hr)) + COMPlusThrowHR(hr, errorResID); + + CorInfoCallConvExtension callConvLocal = builder.GetCurrentCallConv(); + + if (callConvLocal == CallConvBuilder::UnsetValue) + { + callConvLocal = CallConv::GetDefaultUnmanagedCallingConvention(); + } + + return callConvLocal; +} +#endif // DACCESS_COMPILE \ No newline at end of file diff --git a/src/coreclr/vm/siginfo.hpp b/src/coreclr/vm/siginfo.hpp index 37d4be476d8f9e..29930f8b79afab 100644 --- a/src/coreclr/vm/siginfo.hpp +++ b/src/coreclr/vm/siginfo.hpp @@ -1208,6 +1208,8 @@ BOOL CompareTypeDefsForEquivalence(mdToken tk1, mdToken tk2, Module *pModule1, M BOOL IsTypeDefEquivalent(mdToken tk, Module *pModule); BOOL IsTypeDefExternallyVisible(mdToken tk, Module *pModule, DWORD dwAttrs); +CorInfoCallConvExtension GetUnmanagedCallConvExtension(MetaSig* pSig); + void ReportPointersFromValueType(promote_func *fn, ScanContext *sc, PTR_MethodTable pMT, PTR_VOID pSrc); #endif /* _H_SIGINFO */ diff --git a/src/tests/JIT/Regression/JitBlue/GitHub_35384/GitHub_35384.il b/src/tests/JIT/Regression/JitBlue/GitHub_35384/GitHub_35384.il index 65c7948aea41d9..f1141bb09dd6f9 100644 --- a/src/tests/JIT/Regression/JitBlue/GitHub_35384/GitHub_35384.il +++ b/src/tests/JIT/Regression/JitBlue/GitHub_35384/GitHub_35384.il @@ -13,6 +13,18 @@ extends [System.Runtime]System.ValueType { .field public int32 a + .field public int32 b + .field public int32 c + .field public int32 d + .field public int32 e + .field public int32 f + .field public int32 g + .field public int32 h + .field public int32 i + .field public int32 j + .field public int32 k + .field public int32 l + .field public int32 m .method public hidebysig instance string InstanceMethod() cil managed noinlining @@ -22,12 +34,120 @@ ret } // end of method Struct::InstanceMethod + .field public static valuetype Struct s + + .method public hidebysig instance valuetype Struct + InstanceMethodReturningLargeValuetype() cil managed noinlining + { + .maxstack 1 + ldsfld valuetype Struct Struct::s + ret + } // end of method Struct::InstanceMethod } // end of class Struct .class public auto beforefieldinit Program extends [System.Runtime]System.Object { - .method private hidebysig static string + .method private hidebysig static int32 + ValueTypeExplicitThisInstanceMethodCalli_ReturnLargeValuetype() cil managed noinlining + { + .maxstack 3 + .locals init (valuetype Struct V_0, + valuetype Struct V_1) + ldloca.s V_0 + initobj Struct + + ldsflda valuetype Struct Struct::s + ldc.i4.1 + stfld int32 Struct::a + ldsflda valuetype Struct Struct::s + ldc.i4.2 + stfld int32 Struct::b + ldsflda valuetype Struct Struct::s + ldc.i4.3 + stfld int32 Struct::c + ldsflda valuetype Struct Struct::s + ldc.i4.4 + stfld int32 Struct::d + ldsflda valuetype Struct Struct::s + ldc.i4.5 + stfld int32 Struct::e + ldsflda valuetype Struct Struct::s + ldc.i4.6 + stfld int32 Struct::f + ldsflda valuetype Struct Struct::s + ldc.i4.7 + stfld int32 Struct::g + ldsflda valuetype Struct Struct::s + ldc.i4.8 + stfld int32 Struct::h + ldsflda valuetype Struct Struct::s + ldc.i4.s 9 + stfld int32 Struct::i + ldsflda valuetype Struct Struct::s + ldc.i4.s 10 + stfld int32 Struct::j + ldsflda valuetype Struct Struct::s + ldc.i4.s 11 + stfld int32 Struct::k + ldsflda valuetype Struct Struct::s + ldc.i4.s 12 + stfld int32 Struct::l + ldsflda valuetype Struct Struct::s + ldc.i4.s 22 + stfld int32 Struct::m + + ldloca.s V_0 + ldftn instance valuetype Struct Struct::InstanceMethodReturningLargeValuetype() + calli explicit instance valuetype Struct(valuetype Struct&) + + stloc.1 + + ldloca.s V_0 + initobj Struct + + ldloc.1 + ldfld int32 Struct::a + ldloc.1 + ldfld int32 Struct::b + add + ldloc.1 + ldfld int32 Struct::c + add + ldloc.1 + ldfld int32 Struct::d + add + ldloc.1 + ldfld int32 Struct::e + add + ldloc.1 + ldfld int32 Struct::f + add + ldloc.1 + ldfld int32 Struct::g + add + ldloc.1 + ldfld int32 Struct::h + add + ldloc.1 + ldfld int32 Struct::i + add + ldloc.1 + ldfld int32 Struct::j + add + ldloc.1 + ldfld int32 Struct::k + add + ldloc.1 + ldfld int32 Struct::l + add + ldloc.1 + ldfld int32 Struct::m + add + + ret + } // end of method Program::ValueTypeExplicitThisInstanceMethodCalli + .method private hidebysig static string ValueTypeExplicitThisInstanceMethodCalli() cil managed noinlining { .maxstack 2 @@ -43,16 +163,19 @@ .method public hidebysig static int32 Main() cil managed { .custom instance void [xunit.core]Xunit.FactAttribute::.ctor() = {} - .custom instance void [Microsoft.DotNet.XUnitExtensions]Xunit.ActiveIssueAttribute::.ctor(string, class [mscorlib]System.Type, string[]) = { + .custom instance void [Microsoft.DotNet.XUnitExtensions]Xunit.ActiveIssueAttribute::.ctor(string, class [System.Runtime]System.Type, string[]) = { string('https://github.com/dotnet/runtime/issues/114908') type([TestLibrary]TestLibrary.PlatformDetection) - string[1] ('IsAppleMobile') + string[1] ('IsMonoRuntime') } .entrypoint - .maxstack 1 + .maxstack 2 call string Program::ValueTypeExplicitThisInstanceMethodCalli() pop - ldc.i4 100 + ldc.i4 0 + // This should return 100 if it works correctly + call int32 Program::ValueTypeExplicitThisInstanceMethodCalli_ReturnLargeValuetype() + add ret } // end of method Program::Main } // end of class Program From b413110770f08b3e89a7e7d428679470705e644e Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Wed, 17 Dec 2025 10:03:27 -0800 Subject: [PATCH 2/7] Update src/coreclr/vm/callconvbuilder.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/coreclr/vm/callconvbuilder.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/coreclr/vm/callconvbuilder.cpp b/src/coreclr/vm/callconvbuilder.cpp index e64ed7184bfb6b..60d5d5eb0bbe71 100644 --- a/src/coreclr/vm/callconvbuilder.cpp +++ b/src/coreclr/vm/callconvbuilder.cpp @@ -372,6 +372,13 @@ HRESULT CallConv::TryGetUnmanagedCallingConventionFromModOptSigStartingAtRetType _Inout_ CallConvBuilder* builder, _Out_ UINT* errorResID) { + CONTRACTL + { + STANDARD_VM_CHECK; + PRECONDITION(builder != NULL); + PRECONDITION(errorResID != NULL); + } + CONTRACTL_END; PCCOR_SIGNATURE pSig; uint32_t cSig; sig.GetSignature(&pSig, &cSig); From d0027723da02af93eabd8373cb8c101958fece9b Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Wed, 17 Dec 2025 10:03:36 -0800 Subject: [PATCH 3/7] Update src/coreclr/vm/callstubgenerator.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/coreclr/vm/callstubgenerator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/vm/callstubgenerator.cpp b/src/coreclr/vm/callstubgenerator.cpp index 89b2ef42adfa57..553f1df6186564 100644 --- a/src/coreclr/vm/callstubgenerator.cpp +++ b/src/coreclr/vm/callstubgenerator.cpp @@ -2609,7 +2609,7 @@ void CallStubGenerator::ComputeCallStub(MetaSig &sig, PCODE *pRoutines, MethodDe // Skip the explicit this argument sig.NextArg(); - // Copy rest of the arguments + // Copy rest of the arguments sig.NextArg(); SigPointer pArgs = sig.GetArgProps(); for (unsigned i = 1; i < sig.NumFixedArgs(); i++) From c0c191656ad3393e44f7d0ef4a2bd158448a6ec4 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Wed, 17 Dec 2025 10:03:46 -0800 Subject: [PATCH 4/7] Update src/coreclr/vm/callstubgenerator.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/coreclr/vm/callstubgenerator.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/coreclr/vm/callstubgenerator.cpp b/src/coreclr/vm/callstubgenerator.cpp index 553f1df6186564..d53105ff3768a2 100644 --- a/src/coreclr/vm/callstubgenerator.cpp +++ b/src/coreclr/vm/callstubgenerator.cpp @@ -2574,14 +2574,18 @@ void CallStubGenerator::ComputeCallStub(MetaSig &sig, PCODE *pRoutines, MethodDe // in the places where it is needed such as computation of return buffers. if (sig.GetCallingConventionInfo() & IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS) { +#if LOG_COMPUTE_CALL_STUB printf("Managed ExplicitThis to HasThis conversion needed\n"); +#endif // LOG_COMPUTE_CALL_STUB rewriteMetaSigFromExplicitThisToHasThis = true; } SigBuilder sigBuilder; if (rewriteMetaSigFromExplicitThisToHasThis) { +#if LOG_COMPUTE_CALL_STUB printf("Rewriting ExplicitThis to implicit this\n"); +#endif // LOG_COMPUTE_CALL_STUB sigBuilder.AppendByte(IMAGE_CEE_CS_CALLCONV_DEFAULT_HASTHIS); if ((sig.NumFixedArgs() == 0) || (sig.HasThis() && !sig.HasExplicitThis())) { From 7c22c28b34a5ac4f39467cfef1992f8582f0aa11 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Wed, 17 Dec 2025 10:10:00 -0800 Subject: [PATCH 5/7] More code review tweaks --- src/coreclr/vm/callstubgenerator.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/coreclr/vm/callstubgenerator.cpp b/src/coreclr/vm/callstubgenerator.cpp index d53105ff3768a2..36b3fcdc04b0d0 100644 --- a/src/coreclr/vm/callstubgenerator.cpp +++ b/src/coreclr/vm/callstubgenerator.cpp @@ -2508,7 +2508,6 @@ void CallStubGenerator::ComputeCallStub(MetaSig &sig, PCODE *pRoutines, MethodDe { unmanagedCallConv = CorInfoCallConvExtension::C; } - unmanagedCallConv = unmanagedCallConv; } else { From bcd22e4127441e6742cf75da4e331e2381749dfc Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Wed, 17 Dec 2025 10:10:58 -0800 Subject: [PATCH 6/7] Update src/tests/JIT/Regression/JitBlue/GitHub_35384/GitHub_35384.il Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/tests/JIT/Regression/JitBlue/GitHub_35384/GitHub_35384.il | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/JIT/Regression/JitBlue/GitHub_35384/GitHub_35384.il b/src/tests/JIT/Regression/JitBlue/GitHub_35384/GitHub_35384.il index f1141bb09dd6f9..66f085b73e3c35 100644 --- a/src/tests/JIT/Regression/JitBlue/GitHub_35384/GitHub_35384.il +++ b/src/tests/JIT/Regression/JitBlue/GitHub_35384/GitHub_35384.il @@ -42,7 +42,7 @@ .maxstack 1 ldsfld valuetype Struct Struct::s ret - } // end of method Struct::InstanceMethod + } // end of method Struct::InstanceMethodReturningLargeValuetype } // end of class Struct .class public auto beforefieldinit Program From 5beaa44e11af012e4bd109e365f2875b0068c55d Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Wed, 17 Dec 2025 10:11:23 -0800 Subject: [PATCH 7/7] Update src/coreclr/vm/callstubgenerator.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/coreclr/vm/callstubgenerator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/vm/callstubgenerator.cpp b/src/coreclr/vm/callstubgenerator.cpp index 36b3fcdc04b0d0..40095820dd15d4 100644 --- a/src/coreclr/vm/callstubgenerator.cpp +++ b/src/coreclr/vm/callstubgenerator.cpp @@ -2459,7 +2459,7 @@ void CallStubGenerator::TerminateCurrentRoutineIfNotOfNewType(RoutineType type, // it is not a struct at the unmanaged ABI boundary. // // Arguments: -// clsHnd - the handle for the struct type. +// pMT - the handle for the struct type. // // Return Value: // true if the given struct type should be treated as a primitive for unmanaged calls,