Bambosa
❔ How can I provide a safe zero cost abstraction over passing managed function pointers over FFI
I have the following:
Benchmark:
public sealed unsafe partial class Test : IDisposable
{
public delegate void IntVoid(int _);
public static void Callback(int number, IntVoid fn) => callback(number, (delegate* managed<int, void>)Marshal.GetFunctionPointerForDelegate(fn));
public static void Callback2(int number, IntVoid fn) => callback(number, fn);
public static void Callback(int number, delegate* managed<int, void> fn) => callback(number, fn);
[LibraryImport(Native.Unnamed)] private static partial void callback(int number, delegate* managed<int, void> fn);
[LibraryImport(Native.Unnamed)] private static partial void callback(int number, IntVoid fn);
}
public sealed unsafe partial class Test : IDisposable
{
public delegate void IntVoid(int _);
public static void Callback(int number, IntVoid fn) => callback(number, (delegate* managed<int, void>)Marshal.GetFunctionPointerForDelegate(fn));
public static void Callback2(int number, IntVoid fn) => callback(number, fn);
public static void Callback(int number, delegate* managed<int, void> fn) => callback(number, fn);
[LibraryImport(Native.Unnamed)] private static partial void callback(int number, delegate* managed<int, void> fn);
[LibraryImport(Native.Unnamed)] private static partial void callback(int number, IntVoid fn);
}
| Method | Iterations | Mean | Error | StdDev | Allocated |
|--------------- |----------- |--------------:|-------------:|-------------:|----------:|
| CallbackSafe | 10 | 286.22 ns | 3.481 ns | 2.907 ns | - |
| CallbackSafe2 | 10 | 308.83 ns | 1.978 ns | 1.850 ns | - |
| CallbackUnsafe | 10 | 29.99 ns | 0.624 ns | 0.833 ns | - |
| CallbackSafe | 100 | 2,897.19 ns | 46.334 ns | 43.341 ns | - |
| CallbackSafe2 | 100 | 2,894.44 ns | 57.380 ns | 98.978 ns | - |
| CallbackUnsafe | 100 | 289.64 ns | 5.811 ns | 8.697 ns | - |
| CallbackSafe | 1000 | 28,992.22 ns | 575.695 ns | 807.043 ns | - |
| CallbackSafe2 | 1000 | 28,616.33 ns | 522.664 ns | 488.900 ns | - |
| CallbackUnsafe | 1000 | 2,817.02 ns | 54.650 ns | 81.797 ns | - |
| CallbackSafe | 10000 | 287,244.93 ns | 4,654.431 ns | 4,779.758 ns | - |
| CallbackSafe2 | 10000 | 279,869.47 ns | 5,454.815 ns | 4,258.761 ns | - |
| CallbackUnsafe | 10000 | 26,898.52 ns | 80.543 ns | 71.399 ns | - |
| Method | Iterations | Mean | Error | StdDev | Allocated |
|--------------- |----------- |--------------:|-------------:|-------------:|----------:|
| CallbackSafe | 10 | 286.22 ns | 3.481 ns | 2.907 ns | - |
| CallbackSafe2 | 10 | 308.83 ns | 1.978 ns | 1.850 ns | - |
| CallbackUnsafe | 10 | 29.99 ns | 0.624 ns | 0.833 ns | - |
| CallbackSafe | 100 | 2,897.19 ns | 46.334 ns | 43.341 ns | - |
| CallbackSafe2 | 100 | 2,894.44 ns | 57.380 ns | 98.978 ns | - |
| CallbackUnsafe | 100 | 289.64 ns | 5.811 ns | 8.697 ns | - |
| CallbackSafe | 1000 | 28,992.22 ns | 575.695 ns | 807.043 ns | - |
| CallbackSafe2 | 1000 | 28,616.33 ns | 522.664 ns | 488.900 ns | - |
| CallbackUnsafe | 1000 | 2,817.02 ns | 54.650 ns | 81.797 ns | - |
| CallbackSafe | 10000 | 287,244.93 ns | 4,654.431 ns | 4,779.758 ns | - |
| CallbackSafe2 | 10000 | 279,869.47 ns | 5,454.815 ns | 4,258.761 ns | - |
| CallbackUnsafe | 10000 | 26,898.52 ns | 80.543 ns | 71.399 ns | - |
40 replies
❔ P/Invoke x86 ASM
I have the following x86 assembly function:
I am compiling it into a win64 dll using NASM:
Bindings in C#:
Does anyone have any idea what format this should be in for the runtime to accept, I assumed using the same platform architecture and CDECL calling conventions would be enough for this to work.
Thanks
section .text
global _add
_add:
; Arguments are in the registers: rcx and rdx
mov rax, rcx ; Move the first argument from rcx to rax
add rax, rdx ; Add the second argument from rdx to rax
ret ; Return
section .text
global _add
_add:
; Arguments are in the registers: rcx and rdx
mov rax, rcx ; Move the first argument from rcx to rax
add rax, rdx ; Add the second argument from rdx to rax
ret ; Return
nasm -f win64 maths.asm -o maths.dll
The consuming project is set to 64 bit:
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Platforms>x64</Platforms>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Platforms>x64</Platforms>
</PropertyGroup>
public static partial class Maths
{
[LibraryImport(Native.Maths)]
[UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })]
private static partial int add(int a, int b);
public static int Add(int a, int b) => add(a, b);
}
public static partial class Maths
{
[LibraryImport(Native.Maths)]
[UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })]
private static partial int add(int a, int b);
public static int Add(int a, int b) => add(a, b);
}
Unhandled exception. System.BadImageFormatException: An attempt was made to load a program with an incorrect format. (0x8007000B)
at Native.Maths.add(Int32 a, Int32 b)
at Native.Maths.Add(Int32 a, Int32 b) in D:\Code\C#\AssemblyTest\Native\Maths.cs:line 12
at Program.<Main>$(String[] args) in D:\Code\C#\AssemblyTest\Managed\Program.cs:line 3
Unhandled exception. System.BadImageFormatException: An attempt was made to load a program with an incorrect format. (0x8007000B)
at Native.Maths.add(Int32 a, Int32 b)
at Native.Maths.Add(Int32 a, Int32 b) in D:\Code\C#\AssemblyTest\Native\Maths.cs:line 12
at Program.<Main>$(String[] args) in D:\Code\C#\AssemblyTest\Managed\Program.cs:line 3
35 replies