Skip to content

Commit

Permalink
[NativeAOT] win-x86: Calling conventions (#99430)
Browse files Browse the repository at this point in the history
* Use default calling convention for P/Invoke helpers

* More stdcall

* Remove Intrinsic from runtime imports.

* Rework the COOP_PINVOKE_HELPER/PREEMPT_PINVOKE_HELPER macros to take arguments directly instead of tuple and make it work with old MSVC.

* Refactor and rename the runtime import/export macros to FCIMPL/FCIMPLEND, FCDECL, QCIMPL, and QCDECL

* Use numbered FCDECLx/FCIMPLx macros.

* Add NodeFactory.ExternVariable helper

* Drop NATIVEAOT_API from QCalls

* Remove NATIVEAOT_API completely

* Use default calling convention for FEATURE_PERFTRACING QCalls

* Decorate dispatch helpers as STDCALL
  • Loading branch information
filipnavara committed Mar 8, 2024
1 parent 3b8d8da commit da781b3
Show file tree
Hide file tree
Showing 48 changed files with 953 additions and 481 deletions.
67 changes: 41 additions & 26 deletions src/coreclr/nativeaot/Bootstrap/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,39 +103,54 @@ extern "C" bool RhRegisterOSModule(void * pModule,

extern "C" void* PalGetModuleHandleFromPointer(void* pointer);

extern "C" void GetRuntimeException();
extern "C" void RuntimeFailFast();
extern "C" void AppendExceptionStackFrame();
extern "C" void GetSystemArrayEEType();
extern "C" void OnFirstChanceException();
extern "C" void OnUnhandledException();
extern "C" void IDynamicCastableIsInterfaceImplemented();
extern "C" void IDynamicCastableGetInterfaceImplementation();
#if defined(HOST_X86) && defined(HOST_WINDOWS)
#define STRINGIFY(s) #s
#define MANAGED_RUNTIME_EXPORT_ALTNAME(_method) STRINGIFY(/alternatename:_##_method=_method)
#define MANAGED_RUNTIME_EXPORT(_name) \
__pragma(comment (linker, MANAGED_RUNTIME_EXPORT_ALTNAME(_name))) \
extern "C" void __cdecl _name();
#define MANAGED_RUNTIME_EXPORT_NAME(_name) _name
#define CDECL __cdecl
#else
#define MANAGED_RUNTIME_EXPORT(_name) \
extern "C" void _name();
#define MANAGED_RUNTIME_EXPORT_NAME(_name) _name
#define CDECL
#endif

MANAGED_RUNTIME_EXPORT(GetRuntimeException)
MANAGED_RUNTIME_EXPORT(RuntimeFailFast)
MANAGED_RUNTIME_EXPORT(AppendExceptionStackFrame)
MANAGED_RUNTIME_EXPORT(GetSystemArrayEEType)
MANAGED_RUNTIME_EXPORT(OnFirstChanceException)
MANAGED_RUNTIME_EXPORT(OnUnhandledException)
MANAGED_RUNTIME_EXPORT(IDynamicCastableIsInterfaceImplemented)
MANAGED_RUNTIME_EXPORT(IDynamicCastableGetInterfaceImplementation)
#ifdef FEATURE_OBJCMARSHAL
extern "C" void ObjectiveCMarshalTryGetTaggedMemory();
extern "C" void ObjectiveCMarshalGetIsTrackedReferenceCallback();
extern "C" void ObjectiveCMarshalGetOnEnteredFinalizerQueueCallback();
extern "C" void ObjectiveCMarshalGetUnhandledExceptionPropagationHandler();
MANAGED_RUNTIME_EXPORT(ObjectiveCMarshalTryGetTaggedMemory)
MANAGED_RUNTIME_EXPORT(ObjectiveCMarshalGetIsTrackedReferenceCallback)
MANAGED_RUNTIME_EXPORT(ObjectiveCMarshalGetOnEnteredFinalizerQueueCallback)
MANAGED_RUNTIME_EXPORT(ObjectiveCMarshalGetUnhandledExceptionPropagationHandler)
#endif

typedef void(*pfn)();
typedef void (CDECL *pfn)();

static const pfn c_classlibFunctions[] = {
&GetRuntimeException,
&RuntimeFailFast,
&MANAGED_RUNTIME_EXPORT_NAME(GetRuntimeException),
&MANAGED_RUNTIME_EXPORT_NAME(RuntimeFailFast),
nullptr, // &UnhandledExceptionHandler,
&AppendExceptionStackFrame,
&MANAGED_RUNTIME_EXPORT_NAME(AppendExceptionStackFrame),
nullptr, // &CheckStaticClassConstruction,
&GetSystemArrayEEType,
&OnFirstChanceException,
&OnUnhandledException,
&IDynamicCastableIsInterfaceImplemented,
&IDynamicCastableGetInterfaceImplementation,
&MANAGED_RUNTIME_EXPORT_NAME(GetSystemArrayEEType),
&MANAGED_RUNTIME_EXPORT_NAME(OnFirstChanceException),
&MANAGED_RUNTIME_EXPORT_NAME(OnUnhandledException),
&MANAGED_RUNTIME_EXPORT_NAME(IDynamicCastableIsInterfaceImplemented),
&MANAGED_RUNTIME_EXPORT_NAME(IDynamicCastableGetInterfaceImplementation),
#ifdef FEATURE_OBJCMARSHAL
&ObjectiveCMarshalTryGetTaggedMemory,
&ObjectiveCMarshalGetIsTrackedReferenceCallback,
&ObjectiveCMarshalGetOnEnteredFinalizerQueueCallback,
&ObjectiveCMarshalGetUnhandledExceptionPropagationHandler,
&MANAGED_RUNTIME_EXPORT_NAME(ObjectiveCMarshalTryGetTaggedMemory),
&MANAGED_RUNTIME_EXPORT_NAME(ObjectiveCMarshalGetIsTrackedReferenceCallback),
&MANAGED_RUNTIME_EXPORT_NAME(ObjectiveCMarshalGetOnEnteredFinalizerQueueCallback),
&MANAGED_RUNTIME_EXPORT_NAME(ObjectiveCMarshalGetUnhandledExceptionPropagationHandler),
#else
nullptr,
nullptr,
Expand Down Expand Up @@ -198,7 +213,7 @@ static int InitializeRuntime()
#ifndef NATIVEAOT_DLL

#if defined(_WIN32)
int __cdecl wmain(int argc, wchar_t* argv[])
int CDECL wmain(int argc, wchar_t* argv[])
#else
int main(int argc, char* argv[])
#endif
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ internal static partial class StartupCodeHelpers
/// </summary>
private static IntPtr s_moduleGCStaticsSpines;

[UnmanagedCallersOnly(EntryPoint = "InitializeModules", CallConvs = new Type[] { typeof(CallConvCdecl) })]
[UnmanagedCallersOnly(EntryPoint = "InitializeModules")]
internal static unsafe void InitializeModules(IntPtr osModule, IntPtr* pModuleHeaders, int count, IntPtr* pClasslibFunctions, int nClasslibFunctions)
{
RuntimeImports.RhpRegisterOsModule(osModule);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1219,7 +1219,7 @@ private static void InvokeSecondPass(ref ExInfo exInfo, uint idxStart, uint idxL

#if NATIVEAOT
#pragma warning disable IDE0060
[UnmanagedCallersOnly(EntryPoint = "RhpFailFastForPInvokeExceptionPreemp", CallConvs = new Type[] { typeof(CallConvCdecl) })]
[UnmanagedCallersOnly(EntryPoint = "RhpFailFastForPInvokeExceptionPreemp")]
public static void RhpFailFastForPInvokeExceptionPreemp(IntPtr PInvokeCallsiteReturnAddr, void* pExceptionRecord, void* pContextRecord)
{
FailFastViaClasslib(RhFailFastReason.UnhandledExceptionFromPInvoke, null, PInvokeCallsiteReturnAddr);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ internal static void RhCollect(int generation, InternalGCCollectionMode mode, bo
}

[DllImport(Redhawk.BaseName)]
[UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
private static extern void RhpCollect(int generation, InternalGCCollectionMode mode, Interop.BOOL lowMemoryP);

[RuntimeExport("RhGetGcTotalMemory")]
Expand All @@ -76,7 +75,6 @@ internal static long RhGetGcTotalMemory()
}

[DllImport(Redhawk.BaseName)]
[UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
private static extern long RhpGetGcTotalMemory();

[RuntimeExport("RhStartNoGCRegion")]
Expand Down Expand Up @@ -278,28 +276,23 @@ internal static extern unsafe IntPtr RhpCallPropagateExceptionCallback(
// Block the current thread until at least one object needs to be finalized (returns true) or
// memory is low (returns false and the finalizer thread should initiate a garbage collection).
[DllImport(Redhawk.BaseName)]
[UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
internal static extern uint RhpWaitForFinalizerRequest();

// Indicate that the current round of finalizations is complete.
[DllImport(Redhawk.BaseName)]
[UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
internal static extern void RhpSignalFinalizationComplete(uint fCount);

[DllImport(Redhawk.BaseName)]
[UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
internal static extern ulong RhpGetTickCount64();

// Enters a no GC region, possibly doing a blocking GC if there is not enough
// memory available to satisfy the caller's request.
[DllImport(Redhawk.BaseName)]
[UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
internal static extern int RhpStartNoGCRegion(long totalSize, Interop.BOOL hasLohSize, long lohSize, Interop.BOOL disallowFullBlockingGC);

// Exits a no GC region, possibly doing a GC to clean up the garbage that
// the caller allocated.
[DllImport(Redhawk.BaseName)]
[UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
internal static extern int RhpEndNoGCRegion();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,6 @@ public static unsafe int RhGetCurrentThreadStackTrace(IntPtr[] outputBuffer)

#pragma warning disable SYSLIB1054 // Use DllImport here instead of LibraryImport because this file is used by Test.CoreLib.
[DllImport(Redhawk.BaseName)]
[UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
private static extern unsafe int RhpGetCurrentThreadStackTrace(IntPtr* pOutputBuffer, uint outputBufferLength, UIntPtr addressInCurrentFrame);
#pragma warning restore SYSLIB1054

Expand All @@ -303,7 +302,7 @@ public static unsafe int RhGetCurrentThreadStackTrace(IntPtr[] outputBuffer)
// NOTE: We don't want to allocate the array on behalf of the caller because we don't know which class
// library's objects the caller understands (we support multiple class libraries with multiple root
// System.Object types).
[UnmanagedCallersOnly(EntryPoint = "RhpCalculateStackTraceWorker", CallConvs = new Type[] { typeof(CallConvCdecl) })]
[UnmanagedCallersOnly(EntryPoint = "RhpCalculateStackTraceWorker")]
private static unsafe int RhpCalculateStackTraceWorker(IntPtr* pOutputBuffer, uint outputBufferLength, UIntPtr addressInCurrentFrame)
{
uint nFrames = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ namespace System.Runtime
// We choose this name to avoid clashing with any future public class with the name Finalizer.
internal static class __Finalizer
{
[UnmanagedCallersOnly(EntryPoint = "ProcessFinalizers", CallConvs = new Type[] { typeof(CallConvCdecl) })]
[UnmanagedCallersOnly(EntryPoint = "ProcessFinalizers")]
public static void ProcessFinalizers()
{
#if INPLACE_RUNTIME
Expand Down
9 changes: 6 additions & 3 deletions src/coreclr/nativeaot/Runtime/CachedInterfaceDispatch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,7 @@ bool InitializeInterfaceDispatch()
return true;
}

COOP_PINVOKE_HELPER(PCODE, RhpUpdateDispatchCellCache, (InterfaceDispatchCell * pCell, PCODE pTargetCode, MethodTable* pInstanceType, DispatchCellInfo *pNewCellInfo))
FCIMPL4(PCODE, RhpUpdateDispatchCellCache, InterfaceDispatchCell * pCell, PCODE pTargetCode, MethodTable* pInstanceType, DispatchCellInfo *pNewCellInfo)
{
// Attempt to update the cache with this new mapping (if we have any cache at all, the initial state
// is none).
Expand Down Expand Up @@ -515,8 +515,9 @@ COOP_PINVOKE_HELPER(PCODE, RhpUpdateDispatchCellCache, (InterfaceDispatchCell *

return (PCODE)pTargetCode;
}
FCIMPLEND

COOP_PINVOKE_HELPER(PCODE, RhpSearchDispatchCellCache, (InterfaceDispatchCell * pCell, MethodTable* pInstanceType))
FCIMPL2(PCODE, RhpSearchDispatchCellCache, InterfaceDispatchCell * pCell, MethodTable* pInstanceType)
{
// This function must be implemented in native code so that we do not take a GC while walking the cache
InterfaceDispatchCache * pCache = (InterfaceDispatchCache*)pCell->GetCache();
Expand All @@ -530,13 +531,15 @@ COOP_PINVOKE_HELPER(PCODE, RhpSearchDispatchCellCache, (InterfaceDispatchCell *

return (PCODE)nullptr;
}
FCIMPLEND

// Given a dispatch cell, get the type and slot associated with it. This function MUST be implemented
// in cooperative native code, as the m_pCache field on the cell is unsafe to access from managed
// code due to its use of the GC state as a lock, and as lifetime control
COOP_PINVOKE_HELPER(void, RhpGetDispatchCellInfo, (InterfaceDispatchCell * pCell, DispatchCellInfo* pDispatchCellInfo))
FCIMPL2(void, RhpGetDispatchCellInfo, InterfaceDispatchCell * pCell, DispatchCellInfo* pDispatchCellInfo)
{
*pDispatchCellInfo = pCell->GetDispatchCellInfo();
}
FCIMPLEND

#endif // FEATURE_CACHED_INTERFACE_DISPATCH
81 changes: 75 additions & 6 deletions src/coreclr/nativeaot/Runtime/CommonMacros.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
#define STDCALL
#endif

#define NATIVEAOT_API
#define REDHAWK_CALLCONV FASTCALL
#define QCALLTYPE

#ifdef _MSC_VER

Expand Down Expand Up @@ -177,14 +177,83 @@ typedef uint8_t CODE_LOCATION;
// Define an unmanaged function called from managed code that needs to execute in co-operative GC mode. (There
// should be very few of these, most such functions will be simply p/invoked).
//
#define COOP_PINVOKE_HELPER(_rettype, _method, _args) EXTERN_C NATIVEAOT_API _rettype REDHAWK_CALLCONV _method _args
#ifdef HOST_X86
// We have helpers that act like memcpy and memset from the CRT, so they need to be __cdecl.
#define COOP_PINVOKE_CDECL_HELPER(_rettype, _method, _args) EXTERN_C NATIVEAOT_API _rettype __cdecl _method _args

#define FCALL_METHOD_NAME(name, ...) name
#define FCALL_METHOD_NAME_(tuple) FCALL_METHOD_NAME tuple

#if defined(HOST_X86) && defined(HOST_WINDOWS)

// x86 is special case. It supports multiple calling conventions (fastcall, stdcall, cdecl)
// and mangles the method names according to the calling convention (eg. @fastcall@4, _stdcall@4,
// _cdecl).
//
// The managed code uses its own calling convention that is different from the native call
// conventions. It's similar to fastcall but pushes the arguments to stack in reverse order.
// Additionally, for the sake of simplicity we don't decorate the symbol names.
//
// In order to bridge the managed calling convention we use two tricks:
// - The FCIMPL and FCDECL macros reorder parameters for any method with 4 or more arguments.
// - A linker comment is used to pass the "/alternatename:foo=@foo@4" switch to allow the
// symbols to be resolved to the fastcall decorated name.

#define FCALL_ARGHELPER_NAME(_0, _1, _2, _3, _4, _5, NAME, ...) NAME
#define FCALL_ARGHELPER_NAME_(tuple) FCALL_ARGHELPER_NAME tuple

#define FCALL_ARGHELPER0(dummy) ()
#define FCALL_ARGHELPER1(dummy, a) (a)
#define FCALL_ARGHELPER2(dummy, a, b) (a, b)
#define FCALL_ARGHELPER3(dummy, a, b, c) (a, b, c)
#define FCALL_ARGHELPER4(dummy, a, b, c, d) (a, b, d, c)
#define FCALL_ARGHELPER5(dummy, a, b, c, d, e) (a, b, e, d, c)

#define FCALL_STRINGIFY(s) #s
#define FCALL_XSTRINGIFY(s) FCALL_STRINGIFY(s)

#define FCALL_METHOD_ARGS(...) FCALL_ARGHELPER_NAME_((__VA_ARGS__, FCALL_ARGHELPER5, FCALL_ARGHELPER4, FCALL_ARGHELPER3, FCALL_ARGHELPER2, FCALL_ARGHELPER1, FCALL_ARGHELPER0)) (__VA_ARGS__)
#define FCALL_METHOD_ARGS_(tuple) FCALL_METHOD_ARGS tuple

#define FCALL_ARGHELPER_STACKSIZE(...) FCALL_ARGHELPER_NAME_((__VA_ARGS__, 20, 16, 12, 8, 4, 0))
#define FCALL_IMPL_ALTNAME(_method, _argSize) FCALL_XSTRINGIFY(/alternatename:_method=@_method@_argSize)
#define FCALL_DECL_ALTNAME(_method, _argSize) FCALL_XSTRINGIFY(/alternatename:@_method@_argSize=_method)
#define FCDECL_RENAME(_rettype, ...) \
_Pragma(FCALL_XSTRINGIFY(comment (linker, FCALL_DECL_ALTNAME(FCALL_METHOD_NAME_((__VA_ARGS__)), FCALL_ARGHELPER_STACKSIZE(__VA_ARGS__)))))
#define FCIMPL_RENAME(_rettype, ...) \
_Pragma(FCALL_XSTRINGIFY(comment (linker, FCALL_IMPL_ALTNAME(FCALL_METHOD_NAME_((__VA_ARGS__)), FCALL_ARGHELPER_STACKSIZE(__VA_ARGS__)))))

#else
#define COOP_PINVOKE_CDECL_HELPER COOP_PINVOKE_HELPER

#define FCDECL_RENAME(_rettype, ...)
#define FCIMPL_RENAME(_rettype, ...)

#define FCALL_METHOD_ARGS(dummy, ...) (__VA_ARGS__)
#define FCALL_METHOD_ARGS_(tuple) FCALL_METHOD_ARGS tuple

#endif

#define FCDECL_(_rettype, ...) \
FCDECL_RENAME(_rettype, __VA_ARGS__) \
EXTERN_C _rettype REDHAWK_CALLCONV FCALL_METHOD_NAME_((__VA_ARGS__)) FCALL_METHOD_ARGS_((__VA_ARGS__))
#define FCDECL0(_rettype, _method) FCDECL_(_rettype, _method)
#define FCDECL1(_rettype, _method, a) FCDECL_(_rettype, _method, a)
#define FCDECL2(_rettype, _method, a, b) FCDECL_(_rettype, _method, a, b)
#define FCDECL3(_rettype, _method, a, b, c) FCDECL_(_rettype, _method, a, b, c)
#define FCDECL4(_rettype, _method, a, b, c, d) FCDECL_(_rettype, _method, a, b, c, d)
#define FCDECL5(_rettype, _method, a, b, c, d, e) FCDECL_(_rettype, _method, a, b, c, d, e)

#define FCIMPL_(_rettype, ...) \
FCIMPL_RENAME(_rettype, __VA_ARGS__) \
EXTERN_C _rettype REDHAWK_CALLCONV FCALL_METHOD_NAME_((__VA_ARGS__)) FCALL_METHOD_ARGS_((__VA_ARGS__)) \
{
#define FCIMPL0(_rettype, _method) FCIMPL_(_rettype, _method)
#define FCIMPL1(_rettype, _method, a) FCIMPL_(_rettype, _method, a)
#define FCIMPL2(_rettype, _method, a, b) FCIMPL_(_rettype, _method, a, b)
#define FCIMPL3(_rettype, _method, a, b, c) FCIMPL_(_rettype, _method, a, b, c)
#define FCIMPL4(_rettype, _method, a, b, c, d) FCIMPL_(_rettype, _method, a, b, c, d)
#define FCIMPL5(_rettype, _method, a, b, c, d, e) FCIMPL_(_rettype, _method, a, b, c, d, e)

#define FCIMPLEND \
}

typedef bool CLR_BOOL;

#if defined(TARGET_X86) || defined(TARGET_AMD64)
Expand Down
Loading

0 comments on commit da781b3

Please sign in to comment.