【问题标题】:CLI calli on x64 calling conventionx64 调用约定上的 CLI calli
【发布时间】:2018-09-11 09:54:42
【问题描述】:

Calli 操作码需要调用约定。默认是stdcall,而原生库中的extern "C"使用cdecl

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

have read 在 x64 上不再有 stdcallcdecl 的混乱,所有方法都使用 cdecl(或 fastcall,在另一个地方看到,找不到链接)。差异仅适用于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 上“按设计”在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 是什么意思?我的本机函数返回调用后堆栈上的int。这是关于谁从堆栈中删除此int 的问题吗?或者还有一些其他的工作需要在本机函数中使用堆栈完成?我可以控制本机函数,并且可以通过 ref 参数返回值 - 这是否会使堆栈清理问题变得无关紧要,因为在调用期间没有进行堆栈更改?

【问题讨论】:

  • 实际上,运行时仅支持 x64 上的一种调用约定,即“the”调用约定。 cdeclstdcallfastcallthiscall 都是一样的。有关详细信息,请参阅here。返回值在 RAX 寄存器中传递,它们不经过堆栈(除非参数超过 64 位,但这与 CLR 调用无关),但无论如何您不必担心清理堆栈,因为 CLR 会发出适当的代码。
  • @JeroenMostert 等 x64 我总是可以使用可内联的 calli 而没有 unmanaged 和任何调用约定? Linux x64 也是如此?
  • Linux 上的 x64 调用约定与 Windows 不同(使用不同的寄存器),但确实存在“一个”调用约定,所以我希望答案是“是的”。但我没有在 Linux 上使用非托管互操作的经验。
  • @JeroenMostert 它在 Docker 中工作,默认 calli 比 DllImport 快 5 倍(在 VS 的 Release+F5 模式下)。将不得不在 coreclr repo 中询问这是否可以保证工作而不是“发生工作”
  • calli 可用于调用托管和非托管函数。省略 unmanaged 会导致它将函数视为托管函数,如果 JITter 碰巧以相同方式调用具有相同签名的托管函数,这可能起作用,但如果它使用其他调用可能会失败约定(这是一个实现细节)。

标签: c# .net cil calling-convention


【解决方案1】:

答案是,它不安全。在 dotnet/coreclr 上查看此讨论:https://github.com/dotnet/coreclr/issues/19997

【讨论】:

    猜你喜欢
    • 2013-08-10
    • 1970-01-01
    • 2021-08-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-20
    • 2016-05-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多