dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.22k stars 4.72k forks source link

How to Obtain a Method Pointer Without Reflection in NativeAOT? #100103

Closed dadavadd closed 7 months ago

dadavadd commented 7 months ago

Hi!

I am currently exploring NativeAOT for a project that requires high performance and direct method calls without the overhead of reflection. My goal is to obtain a pointer to a specific method in C# to use in an unsafe context, ensuring the most efficient execution possible.

In the traditional .NET runtime, we typically rely on reflection (e.g., MethodInfo.MethodHandle.GetFunctionPointer()) to achieve this, but I understand that reflection might not be fully supported or recommended in NativeAOT for performance and runtime efficiency reasons.

Could you provide guidance or recommendations on how we can obtain a method pointer without relying on reflection within the NativeAOT environment? Are there any specific patterns, practices, or API calls in NativeAOT that facilitate this kind of direct method access?

Any examples or documentation you could point me to would be greatly appreciated.

Thank you for your time and assistance.

dotnet-policy-service[bot] commented 7 months ago

Tagging subscribers to this area: @agocke, @MichalStrehovsky, @jkotas See info in area-owners.md if you want to be subscribed.

MichalStrehovsky commented 7 months ago

The C# function pointers (delegate*) work. If you go to IL level, ldftn works (delegate* is limited to static methods, ldftn isn't).

MethodInfo.MethodHandle.GetFunctionPointer() also works. The only problems with it are trimmability problems - i.e. if you can obtain the MethodInfo in a way that can be statically analyzed, it's not a problem. The trimming can analyze things like typeof(SomeType).GetMethod("Foo"). If you don't see any warnings at publish time (or in IDE when writing the code and the analyzer is enabled either by setting the IsTrimmable or PublishTrimmed property), it's fine.

dadavadd commented 7 months ago

The C# function pointers (delegate*) work. If you go to IL level, ldftn works (delegate* is limited to static methods, ldftn isn't).

MethodInfo.MethodHandle.GetFunctionPointer() also works. The only problems with it are trimmability problems - i.e. if you can obtain the MethodInfo in a way that can be statically analyzed, it's not a problem. The trimming can analyze things like typeof(SomeType).GetMethod("Foo"). If you don't see any warnings at publish time (or in IDE when writing the code and the analyzer is enabled either by setting the IsTrimmable or PublishTrimmed property), it's fine.

I'm trying to port my code to Unity Il2CPP, but reflection is ruining everything. Is it possible to get a pointer to a method without reflection and without necessarily specifying "static"?

dadavadd commented 7 months ago

The C# function pointers (delegate*) work. If you go to IL level, ldftn works (delegate* is limited to static methods, ldftn isn't).

MethodInfo.MethodHandle.GetFunctionPointer() also works. The only problems with it are trimmability problems - i.e. if you can obtain the MethodInfo in a way that can be statically analyzed, it's not a problem. The trimming can analyze things like typeof(SomeType).GetMethod("Foo"). If you don't see any warnings at publish time (or in IDE when writing the code and the analyzer is enabled either by setting the IsTrimmable or PublishTrimmed property), it's fine.

image

At the moment my code looks like this. But it only accepts static functions. Is there a way to get a pointer to more than just static methods without reflection?

MichalStrehovsky commented 7 months ago

Stepping back - once you obtain the function pointer to an instance method, how are you going to invoke it?

dadavadd commented 7 months ago

Stepping back - once you obtain the function pointer to an instance method, how are you going to invoke it?

You are not the first person to ask me about this :). I'm not going to call her. I will store the function instructions into a variable as a byte array :). Is there any way to get a pointer to a non-static method? I just need his address. I'm not trying to call him :)

MichalStrehovsky commented 7 months ago

typeof(TestProtectMethods).GetMethod("SetHealth").MethodHandle.GetFunctionPointer() should work in that case. If the type and method name is visible in the typeof(TestProtectMethods).GetMethod("SetHealth") sequence, there should be no publish-time warnings and therefore no runtime problem.

dadavadd commented 7 months ago

typeof(TestProtectMethods).GetMethod("SetHealth").MethodHandle.GetFunctionPointer()должно работать в этом случае. Если имя типа и метода отображается в typeof(TestProtectMethods).GetMethod("SetHealth")последовательности, не должно быть никаких предупреждений во время публикации и, следовательно, проблем во время выполнения.

THANK YOU!!

dadavadd commented 7 months ago

typeof(TestProtectMethods).GetMethod("SetHealth").MethodHandle.GetFunctionPointer() should work in that case. If the type and method name is visible in the typeof(TestProtectMethods).GetMethod("SetHealth") sequence, there should be no publish-time warnings and therefore no runtime problem.

image image

🤔

MichalStrehovsky commented 7 months ago

Remove the unsupported IlcDisableReflection switch from your project file.

huoyaoyuan commented 7 months ago

I am currently exploring NativeAOT for a project that requires high performance and direct method calls without the overhead of reflection. My goal is to obtain a pointer to a specific method in C# to use in an unsafe context, ensuring the most efficient execution possible.

I'm also curious about this statement. How do you benefit from NativeAOT for your goal?

MichalPetryka commented 7 months ago

How do you benefit from NativeAOT for your goal?

FYI calls on NativeAOT are faster cause they don't go through tiering stubs and such and are just always direct, they're also noticeably faster for indirect calls.

am11 commented 7 months ago

I'm also curious about this statement. How do you benefit from NativeAOT for your goal?

They are porting their code from Il2CPP (Unity's AOT) to .NET NativeAOT backend, so it fits the bill.

dadavadd commented 7 months ago

Remove the unsupported IlcDisableReflection switch from your project file.

I want to check the instructions of given methods and therefore I need pointers without reflection. Because of this, my code doesn't work.

I already know how to get pointers to static methods without reflection. How do I get a pointer to a non-static method without reflection? Sorry for my English, I translate everything through Google Translator

agocke commented 7 months ago

I don't know of any way to do this today without reflection. https://github.com/dotnet/runtime/issues/94975 is one proposal to do so.

agocke commented 7 months ago

Actually, this seems to work fine right now:

unsafe
{
    delegate*<C, void> func = &Accessor.M;
    func(c);
}

static class Accessor
{
    [UnsafeAccessor(UnsafeAccessorKind.Method)]
    public extern static void M(C c);
}

public class C {
    public void M() {
        Console.WriteLine("Hello");
    }
}
MichalPetryka commented 7 months ago

Actually, this seems to work fine right now

Worth noting that this returns a pointer to a wrapper static method, not the method itself.

MichalStrehovsky commented 7 months ago

I want to check the instructions of given methods and therefore I need pointers without reflection. Because of this, my code doesn't work.

You have to use reflection or write IL code by hand. The ldftn IL instruction returns an address of instance methods. Otherwise just remove the unsupported IlcDisableReflection and use reflection.

Worth noting that this returns a pointer to a wrapper static method, not the method itself.

Yes, that example returns the address of Accessor.M instead of C.M

I think with this we exhausted the list of options.