基于 x64 调用约定的 CLI 调用

CLI calli on x64 calling convention

Calli 操作码需要调用约定。默认情况下它是 stdcall,而本机库中的 extern "C" 使用 cdecl.

JIT recently allowed 使用 calli 内联方法,但仅使用 默认调用约定 。当我使用 calli 而不使用 unmanaged cdecl 调用方法时,它在 x64 上运行,性能比 DllImport 快 58%,比 unmanaged function pointer 快 2.2 倍。 (在 netcoreapp2.1 上,在 net471 上差异更大:82% 和 5.5x )当我 运行 使用 calli unmanaged cdecl 的方法时,性能与 [=18= 相当](大约慢 1%)。

have read 在 x64 上不再有 stdcallcdecl 的混乱,所有方法都使用 cdecl(或 fastcall,看到在另一个地方,找不到 link)。差异仅适用于 x86,其中我没有 unmanaged cdecl 的调用确实使应用程序因段错误而崩溃。

有问题的方法是the following。对于测试,我仅使用 noop 本机方法来测量本机调用开销。

  .method public hidebysig static int32 CalliCompress(uint8* source, native int sourceLength, uint8* destination, native int destinationLength, int32 clevel, native int functionPtr) cil managed aggressiveinlining
  {
    .custom instance void System.Runtime.Versioning.NonVersionableAttribute::.ctor()
             = {}
    // 
    .maxstack  6
    ldarg.0
    ldarg.1
    ldarg.2
    ldarg.3
    ldarg 4
    ldarg 5
    calli unmanaged cdecl int32 (uint8* source, native int sourceLength, uint8* destination, native int destinationLength, int32 clevel) 
    ret
  }

我的问题:

1) 在 x64 "by design" 上的 calli 之后省略 unmanaged cdecl 是否安全,或者我对这个例子很幸运?如果在 x64 上所有调用都是 cdecl 那么我可以使用 JIT treating static readonly fields as constants 仅使用 if(IntPtr.Size == 8) {..call fast method..}else{..use unmanaged cdecl..}

免费分派到适当的方法

2) caller or callee cleans the stack 是什么意思?我的本机函数 return 是调用后位于堆栈上的 int。这是关于谁从堆栈中删除此 int 的问题吗?或者还有一些其他的工作需要在本机函数中使用堆栈来完成?我控制着本机函数并且可以 return 通过 ref 参数的值 - 这是否会使堆栈清理问题变得无关紧要,因为在调用期间没有进行堆栈更改?

答案是,不安全。请参阅 dotnet/coreclr 上的讨论:https://github.com/dotnet/coreclr/issues/19997