Managed to implement calls with IL calli opcodes, both to managed and unmanaged function pointers.
This tricks could be used to manual function devirtualizing and calling managed functions without reflection and GC/boxing allocations.
FuncUnity wrapper is perfect to use within Unity because IL2CPP RuntimeMethodHandle.Value is an ACTUAL function pointer, and in Mono land this structure contains GetFunctionPointer(). This wrapper expects IL2CPP function pointer to be marked with (funcPtr | 0x80000000000000), that's beyond application address space, so we are safe here.
Of course it's possible to use FuncManaged with IL2CPP, but it could be not peak perf tho.
This tricks could be used to manual function devirtualizing and calling managed functions without reflection and GC/boxing allocations.
FuncUnity wrapper is perfect to use within Unity because IL2CPP RuntimeMethodHandle.Value is an ACTUAL function pointer, and in Mono land this structure contains GetFunctionPointer(). This wrapper expects IL2CPP function pointer to be marked with (funcPtr | 0x80000000000000), that's beyond application address space, so we are safe here.
Of course it's possible to use FuncManaged with IL2CPP, but it could be not peak perf tho.
Working on cross-runtime unsafe library for C# and Unity.
Right now we are having:
ArrayAccess: unsafe access to managed arrays (type replacement, getting data pointers etc). It's also possible to create non-GC array with the custom allocator.
ListAccess: array replacement, thread-safe Add, non-erasure count changing)
StringAccess: mutating the string, copying from ASCII or any other encoding, changing length, etc.
ObjectAccess: basically MonoObject or CLR object header. It's possible to change VTable right now.
Right now we are having:
ArrayAccess: unsafe access to managed arrays (type replacement, getting data pointers etc). It's also possible to create non-GC array with the custom allocator.
ListAccess: array replacement, thread-safe Add, non-erasure count changing)
StringAccess: mutating the string, copying from ASCII or any other encoding, changing length, etc.
ObjectAccess: basically MonoObject or CLR object header. It's possible to change VTable right now.
Finally managed to create a generic solution for direct function calling. This will even work under Burst, because it allows to use calli unmanaged cdecl calls, and .NET runtime can provide us this with
Tho this call creates some wrapper and reverse P/Invoke call, on IL2CPP it's absolutely useless, so we can call this function directly.
For some reason IL2CPP doesn't compile the code where TReturn is returned by ref with Fully Shared Generics enabled, this is why I'm using RefReturn struct to overcome this behavior.
P.S. Different platforms using different function pointer alignment, WebGL doesn't restricts function to be aligned by 1 byte, which means I can't use first bit to store
Marshal.GetFunctionPointerForDelegate
.Tho this call creates some wrapper and reverse P/Invoke call, on IL2CPP it's absolutely useless, so we can call this function directly.
FuncIL2CPP/FuncIL2CPPStatic
provides us this ability and we can safely use it within burst and call managed land functions without any conversion.GetUnityCaller()
returns a special call which can directly call IL2CPP methods without jumping to RuntimeMethodHandle->methodPtr
again in again. This caller works in cross-runtime mode, so you can use it without unity without any limitations, tho it will give very little performance penalty.For some reason IL2CPP doesn't compile the code where TReturn is returned by ref with Fully Shared Generics enabled, this is why I'm using RefReturn struct to overcome this behavior.
P.S. Different platforms using different function pointer alignment, WebGL doesn't restricts function to be aligned by 1 byte, which means I can't use first bit to store
isIL2CPP
flag, while Android ARMv7 (32bit) allows functions to store last bit, so I can't use it either. So for Unity IL2CPP direct calls I've made 64-bit field for funcPtr (even on 32-bit platforms) and use last (0x8000000000000000
) bit as indicator for IL2CPP direct call. To make direct il2cpp calls faster it's possible to reinterpret to AsIL2CPP()
if you are sure that you are running on IL2CPP.#if ENABLE_IL2CPP
FuncIL2CPP func = unityCall.AsIL2CPP();
#else
FuncManaged func = unityCall.AsManaged();
#endif
👏3🔥2
Unity have implemented fully generic shared function generation, when generic params could be both reference types or struct types to generate less code, but when
And by "fully" I mean FULLY. Yes, with returning value too. Then it referencing
calli
opcode is encountered with an unmanaged call it will generate exception. Though it's ok because we don't need fully generic sharing, we always know argument types upfront. This exception throw will fully replace the call.And by "fully" I mean FULLY. Yes, with returning value too. Then it referencing
intptr_t L_11
which should be a return value, and since it's now non-existent, well, the variable became non-existent too leading to compile errors. This is only the case when TReturn is a reference type, of there's no TReturn at all (returning a void* for instance)https://github.com/Meetem/ILCall
ILCall is now released at version 1.0.0 🎉🎉
You can make your IL additions by using provided sln to generate new ILCall.dll.
Binaries could be found in "Releases" section on GitHub.
ILCall is now released at version 1.0.0 🎉🎉
You can make your IL additions by using provided sln to generate new ILCall.dll.
Binaries could be found in "Releases" section on GitHub.
GitHub
GitHub - Meetem/ILCall
Contribute to Meetem/ILCall development by creating an account on GitHub.
👍6
A little example on how to use IL to our profit and how to overcome C# limitations.
In this case I'll show you how to call an arbitrary function by a pointer.
https://meetemq.com/2023/09/06/saddling-up-il-to-our-benefit/
In this case I'll show you how to call an arbitrary function by a pointer.
https://meetemq.com/2023/09/06/saddling-up-il-to-our-benefit/
Meetemq
Saddling up IL to our benefit
When you come from C++ to C# it’s such a relief. C# is a greatly designed language, you can do almost anything you want with it, apart from some design choises like macroses and C++ template<>. But sometimes we can find ourselves near the wall which you can’t…
🔥3
An article on getting field offset of struct or class object, and how to make it cross-runtime, so it works in Unity (Mono/IL2CPP) and real dotnets.
Field offsets are good stuff to read/write fields without incurring into reflection, so we can avoid TypedReferences (not supported in IL2CPP) and also we avoid GC allocations for fieldInfo.SetValue(object target, object value), if both target and value are structs that's two boxing operations!
Let's dive in!
https://meetemq.com/2023/09/10/nets-fields-and-their-offsets/
Field offsets are good stuff to read/write fields without incurring into reflection, so we can avoid TypedReferences (not supported in IL2CPP) and also we avoid GC allocations for fieldInfo.SetValue(object target, object value), if both target and value are structs that's two boxing operations!
Let's dive in!
https://meetemq.com/2023/09/10/nets-fields-and-their-offsets/
Meetemq
.NET(s) Fields and their offsets
Field offsets can help you in numerous ways, first what comes in mind is that you can set fields of the struct or class instance without using TypedReference (doesn't work in IL2CPP) and without GC allocs which comes with boxing when either instance or a…
🔥5
How do I know if I'm running under Mono or .NETFX, or may be Burst, should I say .NET Core?
Here's how you can tell it at JIT/Compile-time!
https://meetemq.com/2023/09/12/detecting-net-runtime-at-compile-www.tgoop.com/
Here's how you can tell it at JIT/Compile-time!
https://meetemq.com/2023/09/12/detecting-net-runtime-at-compile-www.tgoop.com/
Meetemq
Detecting .NET runtime at compile time
When you want to have single DLL which works in all .NET runtimes (Mono/IL2CPP/NETFX/NETCore), it's crucial to detect which runtime you are currently in. While there are some dynamic methods, like query runtime name and check it for "Mono" string, it's not…
👍4
Запустил стрим, подпиливаем и пересобираем dotnet-runtime, буду юзать его для кастомного движка в поддержкой шарпа
https://www.youtube.com/live/U57DD1g6_nA?feature=shared
https://www.youtube.com/live/U57DD1g6_nA?feature=shared
❤9👍1
MadSharp: Unsafe
Запустил стрим, подпиливаем и пересобираем dotnet-runtime, буду юзать его для кастомного движка в поддержкой шарпа https://www.youtube.com/live/U57DD1g6_nA?feature=shared
Тестовый стрим имхо прошел не плохо. Правда задачу решить не получилось, буду дальше думать))
👍6