Skip to content

Commit 01d724c

Browse files
jkoritzinskyjkotas
andauthored
Add CoreCLR implementation of CallConvMemberFunction. (#47828)
* Add CoreCLR implementation of CallConvMemberFunction. * Add tests and make a few fixes to get them passing. * Fix Clang build * Fix argument ordering for calling a cdecl or stdcall instance method from managed. * Update src/coreclr/dlls/mscorrc/mscorrc.rc Co-authored-by: Jan Kotas <[email protected]> * Fix UnmanagedCallersOnly instance method calls for stdcall and cdecl. * Update issues.targets. * Fix formatting. * Add dummy param to validate param order. Rewrite traversal to second-to-last argument. * Merge implementations of call conv modopt name comparison. Use a macro-based implementation initially since it allows us to add new calling conventions by adding one line and the macro-ized code is relatively small. * Fix assignment alignment. * SAL and contracts. * Add assert * Fix UnmanagedCallersOnly parsing in the refactored parser. Co-authored-by: Jan Kotas <[email protected]>
1 parent bb25166 commit 01d724c

File tree

29 files changed

+1127
-111
lines changed

29 files changed

+1127
-111
lines changed

src/coreclr/dlls/mscorrc/mscorrc.rc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,7 @@ BEGIN
413413

414414
IDS_INVALID_REDIM "Illegal attempt to replace or redimension a fixed or locked SafeArray."
415415

416-
IDS_INVALID_PINVOKE_CALLCONV "Invalid unmanaged calling convention: must be one of stdcall, cdecl, or thiscall."
416+
IDS_INVALID_PINVOKE_CALLCONV "Unsupported unmanaged calling convention."
417417
IDS_CLASSLOAD_NSTRUCT_EXPLICIT_OFFSET "Could not load type '%1' from assembly '%2' because field '%3' was not given an explicit offset."
418418
IDS_WRONGSIZEARRAY_IN_NSTRUCT "Type could not be marshaled because the length of an embedded array instance does not match the declared length in the layout."
419419

src/coreclr/inc/corhdr.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1894,6 +1894,7 @@ typedef enum LoadHintEnum
18941894
#define CMOD_CALLCONV_NAME_THISCALL "CallConvThiscall"
18951895
#define CMOD_CALLCONV_NAME_FASTCALL "CallConvFastcall"
18961896
#define CMOD_CALLCONV_NAME_SUPPRESSGCTRANSITION "CallConvSuppressGCTransition"
1897+
#define CMOD_CALLCONV_NAME_MEMBERFUNCTION "CallConvMemberFunction"
18971898

18981899
#endif // MACROS_NOT_SUPPORTED
18991900

src/coreclr/inc/corinfo.h

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -595,7 +595,7 @@ enum CorInfoHelpFunc
595595
CORINFO_HELP_JIT_PINVOKE_BEGIN, // Transition to preemptive mode before a P/Invoke, frame is the first argument
596596
CORINFO_HELP_JIT_PINVOKE_END, // Transition to cooperative mode after a P/Invoke, frame is the first argument
597597

598-
CORINFO_HELP_JIT_REVERSE_PINVOKE_ENTER, // Transition to cooperative mode in reverse P/Invoke prolog, frame is the first argument
598+
CORINFO_HELP_JIT_REVERSE_PINVOKE_ENTER, // Transition to cooperative mode in reverse P/Invoke prolog, frame is the first argument
599599
CORINFO_HELP_JIT_REVERSE_PINVOKE_ENTER_TRACK_TRANSITIONS, // Transition to cooperative mode and track transitions in reverse P/Invoke prolog.
600600
CORINFO_HELP_JIT_REVERSE_PINVOKE_EXIT, // Transition to preemptive mode in reverse P/Invoke epilog, frame is the first argument
601601
CORINFO_HELP_JIT_REVERSE_PINVOKE_EXIT_TRACK_TRANSITIONS, // Transition to preemptive mode and track transitions in reverse P/Invoke prolog.
@@ -711,25 +711,28 @@ enum class CorInfoCallConvExtension
711711
C,
712712
Stdcall,
713713
Thiscall,
714-
Fastcall
714+
Fastcall,
715715
// New calling conventions supported with the extensible calling convention encoding go here.
716+
CMemberFunction,
717+
StdcallMemberFunction,
718+
FastcallMemberFunction
716719
};
717720

718721
#ifdef TARGET_X86
719722
inline bool IsCallerPop(CorInfoCallConvExtension callConv)
720723
{
721724
#ifdef UNIX_X86_ABI
722-
return callConv == CorInfoCallConvExtension::Managed || callConv == CorInfoCallConvExtension::C;
725+
return callConv == CorInfoCallConvExtension::Managed || callConv == CorInfoCallConvExtension::C || callConv == CorInfoCallConvExtension::CMemberFunction;
723726
#else
724-
return callConv == CorInfoCallConvExtension::C;
727+
return callConv == CorInfoCallConvExtension::C || callConv == CorInfoCallConvExtension::CMemberFunction;
725728
#endif // UNIX_X86_ABI
726729
}
727730
#endif
728731

729732
// Determines whether or not this calling convention is an instance method calling convention.
730733
inline bool callConvIsInstanceMethodCallConv(CorInfoCallConvExtension callConv)
731734
{
732-
return callConv == CorInfoCallConvExtension::Thiscall;
735+
return callConv == CorInfoCallConvExtension::Thiscall || callConv == CorInfoCallConvExtension::CMemberFunction || callConv == CorInfoCallConvExtension::StdcallMemberFunction || callConv == CorInfoCallConvExtension::FastcallMemberFunction;
733736
}
734737

735738
// These are returned from getMethodOptions

src/coreclr/jit/importer.cpp

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1311,7 +1311,41 @@ GenTree* Compiler::impAssignStructPtr(GenTree* destAddr,
13111311
{
13121312
if (callConvIsInstanceMethodCallConv(srcCall->GetUnmanagedCallConv()))
13131313
{
1314+
#ifdef TARGET_X86
1315+
// The argument list has already been reversed.
1316+
// Insert the return buffer as the second-to-last node
1317+
// so it will be pushed on to the stack after the user args but before the native this arg
1318+
// as required by the native ABI.
1319+
GenTreeCall::Use* lastArg = srcCall->gtCallArgs;
1320+
if (lastArg == nullptr)
1321+
{
1322+
srcCall->gtCallArgs = gtPrependNewCallArg(destAddr, srcCall->gtCallArgs);
1323+
}
1324+
else if (srcCall->GetUnmanagedCallConv() == CorInfoCallConvExtension::Thiscall)
1325+
{
1326+
// For thiscall, the "this" parameter is not included in the argument list reversal,
1327+
// so we need to put the return buffer as the last parameter.
1328+
for (; lastArg->GetNext() != nullptr; lastArg = lastArg->GetNext())
1329+
;
1330+
gtInsertNewCallArgAfter(destAddr, lastArg);
1331+
}
1332+
else if (lastArg->GetNext() == nullptr)
1333+
{
1334+
srcCall->gtCallArgs = gtPrependNewCallArg(destAddr, lastArg);
1335+
}
1336+
else
1337+
{
1338+
assert(lastArg != nullptr && lastArg->GetNext() != nullptr);
1339+
GenTreeCall::Use* secondLastArg = lastArg;
1340+
lastArg = lastArg->GetNext();
1341+
for (; lastArg->GetNext() != nullptr; secondLastArg = lastArg, lastArg = lastArg->GetNext())
1342+
;
1343+
assert(secondLastArg->GetNext() != nullptr);
1344+
gtInsertNewCallArgAfter(destAddr, secondLastArg);
1345+
}
1346+
#else
13141347
GenTreeCall::Use* thisArg = gtInsertNewCallArgAfter(destAddr, srcCall->gtCallArgs);
1348+
#endif
13151349
}
13161350
else
13171351
{
@@ -7151,8 +7185,11 @@ void Compiler::impCheckForPInvokeCall(
71517185
call->gtCallMoreFlags |= GTF_CALL_M_SUPPRESS_GC_TRANSITION;
71527186
}
71537187

7154-
if (unmanagedCallConv != CorInfoCallConvExtension::C && unmanagedCallConv != CorInfoCallConvExtension::Stdcall &&
7155-
unmanagedCallConv != CorInfoCallConvExtension::Thiscall)
7188+
// If we can't get the unmanaged calling convention or the calling convention is unsupported in the JIT,
7189+
// return here without inlining the native call.
7190+
if (unmanagedCallConv == CorInfoCallConvExtension::Managed ||
7191+
unmanagedCallConv == CorInfoCallConvExtension::Fastcall ||
7192+
unmanagedCallConv == CorInfoCallConvExtension::FastcallMemberFunction)
71567193
{
71577194
return;
71587195
}
@@ -7216,7 +7253,8 @@ void Compiler::impCheckForPInvokeCall(
72167253
}
72177254

72187255
// AMD64 convention is same for native and managed
7219-
if (unmanagedCallConv == CorInfoCallConvExtension::C)
7256+
if (unmanagedCallConv == CorInfoCallConvExtension::C ||
7257+
unmanagedCallConv == CorInfoCallConvExtension::CMemberFunction)
72207258
{
72217259
call->gtFlags |= GTF_CALL_POP_ARGS;
72227260
}

src/coreclr/jit/lclvars.cpp

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -247,10 +247,13 @@ void Compiler::lvaInitTypeRef()
247247
break;
248248
case CorInfoCallConvExtension::C:
249249
case CorInfoCallConvExtension::Stdcall:
250+
case CorInfoCallConvExtension::CMemberFunction:
251+
case CorInfoCallConvExtension::StdcallMemberFunction:
250252
varDscInfo.Init(lvaTable, hasRetBuffArg, 0, 0);
251253
break;
252254
case CorInfoCallConvExtension::Managed:
253255
case CorInfoCallConvExtension::Fastcall:
256+
case CorInfoCallConvExtension::FastcallMemberFunction:
254257
default:
255258
varDscInfo.Init(lvaTable, hasRetBuffArg, MAX_REG_ARG, MAX_FLOAT_REG_ARG);
256259
break;
@@ -5482,9 +5485,13 @@ void Compiler::lvaAssignVirtualFrameOffsetsToArgs()
54825485
// the native return buffer parameter.
54835486
if (callConvIsInstanceMethodCallConv(info.compCallConv))
54845487
{
5485-
noway_assert(lvaTable[lclNum].lvIsRegArg);
5486-
#ifndef TARGET_X86
5487-
argOffs = lvaAssignVirtualFrameOffsetToArg(lclNum, REGSIZE_BYTES, argOffs);
5488+
#ifdef TARGET_X86
5489+
if (!lvaTable[lclNum].lvIsRegArg)
5490+
{
5491+
argOffs = lvaAssignVirtualFrameOffsetToArg(lclNum, REGSIZE_BYTES, argOffs);
5492+
}
5493+
#else
5494+
argOffs = lvaAssignVirtualFrameOffsetToArg(lclNum, REGSIZE_BYTES, argOffs);
54885495
#endif // TARGET_X86
54895496
lclNum++;
54905497
userArgsToSkip++;

src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,7 @@ private CorInfoCallConvExtension GetUnmanagedCallingConventionFromAttribute(Cust
564564
}
565565

566566
bool found = false;
567+
bool memberFunctionVariant = false;
567568
foreach (CustomAttributeTypedArgument<TypeDesc> type in callConvArray)
568569
{
569570
if (!(type.Value is DefType defType))
@@ -572,6 +573,12 @@ private CorInfoCallConvExtension GetUnmanagedCallingConventionFromAttribute(Cust
572573
if (defType.Namespace != "System.Runtime.CompilerServices")
573574
continue;
574575

576+
if (defType.Name == "CallConvMemberFunction")
577+
{
578+
memberFunctionVariant = true;
579+
continue;
580+
}
581+
575582
CorInfoCallConvExtension? callConvLocal = GetCallingConventionForCallConvType(defType);
576583

577584
if (callConvLocal.HasValue)
@@ -584,6 +591,12 @@ private CorInfoCallConvExtension GetUnmanagedCallingConventionFromAttribute(Cust
584591
found = true;
585592
}
586593
}
594+
595+
if (found && memberFunctionVariant)
596+
{
597+
callConv = GetMemberFunctionCallingConventionVariant(callConv);
598+
}
599+
587600
return callConv;
588601
}
589602

@@ -595,6 +608,7 @@ private bool TryGetUnmanagedCallingConventionFromModOpt(MethodSignature signatur
595608
return false;
596609

597610
bool found = false;
611+
bool memberFunctionVariant = false;
598612
foreach (EmbeddedSignatureData data in signature.GetEmbeddedSignatureData())
599613
{
600614
if (data.kind != EmbeddedSignatureDataKind.OptionalCustomModifier)
@@ -616,6 +630,11 @@ private bool TryGetUnmanagedCallingConventionFromModOpt(MethodSignature signatur
616630
suppressGCTransition = true;
617631
continue;
618632
}
633+
else if (defType.Name == "CallConvMemberFunction")
634+
{
635+
memberFunctionVariant = true;
636+
continue;
637+
}
619638

620639
CorInfoCallConvExtension? callConvLocal = GetCallingConventionForCallConvType(defType);
621640

@@ -630,6 +649,11 @@ private bool TryGetUnmanagedCallingConventionFromModOpt(MethodSignature signatur
630649
}
631650
}
632651

652+
if (found && memberFunctionVariant)
653+
{
654+
callConv = GetMemberFunctionCallingConventionVariant(callConv);
655+
}
656+
633657
return found;
634658
}
635659

@@ -644,6 +668,15 @@ private bool TryGetUnmanagedCallingConventionFromModOpt(MethodSignature signatur
644668
_ => null
645669
};
646670

671+
private static CorInfoCallConvExtension GetMemberFunctionCallingConventionVariant(CorInfoCallConvExtension baseCallConv) =>
672+
baseCallConv switch
673+
{
674+
CorInfoCallConvExtension.C => CorInfoCallConvExtension.CMemberFunction,
675+
CorInfoCallConvExtension.Stdcall => CorInfoCallConvExtension.StdcallMemberFunction,
676+
CorInfoCallConvExtension.Fastcall => CorInfoCallConvExtension.FastcallMemberFunction,
677+
var c => c
678+
};
679+
647680
private void Get_CORINFO_SIG_INFO(MethodSignature signature, CORINFO_SIG_INFO* sig)
648681
{
649682
sig->callConv = (CorInfoCallConv)(signature.Flags & MethodSignatureFlags.UnmanagedCallingConventionMask);

src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -357,8 +357,11 @@ enum CorInfoCallConvExtension
357357
C,
358358
Stdcall,
359359
Thiscall,
360-
Fastcall
360+
Fastcall,
361361
// New calling conventions supported with the extensible calling convention encoding go here.
362+
CMemberFunction,
363+
StdcallMemberFunction,
364+
FastcallMemberFunction
362365
}
363366

364367
public enum CORINFO_CALLINFO_FLAGS

src/coreclr/vm/corelib.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -777,6 +777,7 @@ DEFINE_CLASS(CALLCONV_STDCALL, CompilerServices, CallConvStd
777777
DEFINE_CLASS(CALLCONV_THISCALL, CompilerServices, CallConvThiscall)
778778
DEFINE_CLASS(CALLCONV_FASTCALL, CompilerServices, CallConvFastcall)
779779
DEFINE_CLASS(CALLCONV_SUPPRESSGCTRANSITION, CompilerServices, CallConvSuppressGCTransition)
780+
DEFINE_CLASS(CALLCONV_MEMBERFUNCTION, CompilerServices, CallConvMemberFunction)
780781

781782
DEFINE_CLASS_U(Interop, SafeHandle, SafeHandle)
782783
DEFINE_FIELD_U(handle, SafeHandle, m_handle)

src/coreclr/vm/dllimport.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4037,9 +4037,9 @@ static void CreateNDirectStubAccessMetadata(
40374037
}
40384038
else
40394039
{
4040-
if (unmgdCallConv != CorInfoCallConvExtension::Stdcall &&
4041-
unmgdCallConv != CorInfoCallConvExtension::C &&
4042-
unmgdCallConv != CorInfoCallConvExtension::Thiscall)
4040+
if (unmgdCallConv == CorInfoCallConvExtension::Managed ||
4041+
unmgdCallConv == CorInfoCallConvExtension::Fastcall ||
4042+
unmgdCallConv == CorInfoCallConvExtension::FastcallMemberFunction)
40434043
{
40444044
COMPlusThrow(kTypeLoadException, IDS_INVALID_PINVOKE_CALLCONV);
40454045
}

src/coreclr/vm/dllimportcallback.cpp

Lines changed: 24 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -556,22 +556,6 @@ void STDCALL LogUMTransition(UMEntryThunk* thunk)
556556
}
557557
#endif
558558

559-
namespace
560-
{
561-
// Templated function to compute if a char string begins with a constant string.
562-
template<size_t S2LEN>
563-
bool BeginsWith(ULONG s1Len, const char* s1, const char (&s2)[S2LEN])
564-
{
565-
WRAPPER_NO_CONTRACT;
566-
567-
ULONG s2Len = (ULONG)S2LEN - 1; // Remove null
568-
if (s1Len < s2Len)
569-
return false;
570-
571-
return (0 == strncmp(s1, s2, s2Len));
572-
}
573-
}
574-
575559
bool TryGetCallingConventionFromUnmanagedCallersOnly(MethodDesc* pMD, CorInfoCallConvExtension* pCallConv)
576560
{
577561
STANDARD_VM_CONTRACT;
@@ -643,34 +627,39 @@ bool TryGetCallingConventionFromUnmanagedCallersOnly(MethodDesc* pMD, CorInfoCal
643627
else
644628
{
645629
// Set WinAPI as the default
646-
callConvLocal = MetaSig::GetDefaultUnmanagedCallingConvention();
630+
callConvLocal = CorInfoCallConvExtension::Managed;
631+
632+
MetaSig::CallingConventionModifiers modifiers = MetaSig::CALL_CONV_MOD_NONE;
633+
634+
bool foundBaseCallConv = false;
635+
bool useMemberFunctionVariant = false;
647636

648637
CaValue* arrayOfTypes = &namedArgs[0].val;
649638
for (ULONG i = 0; i < arrayOfTypes->arr.length; i++)
650639
{
651640
CaValue& typeNameValue = arrayOfTypes->arr[i];
652641

653-
// According to ECMA-335, type name strings are UTF-8. Since we are
654-
// looking for type names that are equivalent in ASCII and UTF-8,
655-
// using a const char constant is acceptable. Type name strings are
656-
// in Fully Qualified form, so we include the ',' delimiter.
657-
if (BeginsWith(typeNameValue.str.cbStr, typeNameValue.str.pStr, "System.Runtime.CompilerServices.CallConvCdecl,"))
658-
{
659-
callConvLocal = CorInfoCallConvExtension::C;
660-
}
661-
else if (BeginsWith(typeNameValue.str.cbStr, typeNameValue.str.pStr, "System.Runtime.CompilerServices.CallConvStdcall,"))
662-
{
663-
callConvLocal = CorInfoCallConvExtension::Stdcall;
664-
}
665-
else if (BeginsWith(typeNameValue.str.cbStr, typeNameValue.str.pStr, "System.Runtime.CompilerServices.CallConvFastcall,"))
642+
if (!MetaSig::TryApplyModOptToCallingConvention(
643+
typeNameValue.str.pStr,
644+
typeNameValue.str.cbStr,
645+
MetaSig::CallConvModOptNameType::FullyQualifiedName,
646+
&callConvLocal,
647+
&modifiers))
666648
{
667-
callConvLocal = CorInfoCallConvExtension::Fastcall;
668-
}
669-
else if (BeginsWith(typeNameValue.str.cbStr, typeNameValue.str.pStr, "System.Runtime.CompilerServices.CallConvThiscall,"))
670-
{
671-
callConvLocal = CorInfoCallConvExtension::Thiscall;
649+
// We found a second base calling convention.
650+
return false;
672651
}
673652
}
653+
654+
if (callConvLocal == CorInfoCallConvExtension::Managed)
655+
{
656+
callConvLocal = MetaSig::GetDefaultUnmanagedCallingConvention();
657+
}
658+
659+
if (modifiers & MetaSig::CALL_CONV_MOD_MEMBERFUNCTION)
660+
{
661+
callConvLocal = MetaSig::GetMemberFunctionUnmanagedCallingConventionVariant(callConvLocal);
662+
}
674663
}
675664
*pCallConv = callConvLocal;
676665
return true;

0 commit comments

Comments
 (0)