Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NativeAOT delegate creation helper call blocks Escape Analysis #110847

Open
MichalPetryka opened this issue Dec 20, 2024 · 3 comments
Open

NativeAOT delegate creation helper call blocks Escape Analysis #110847

MichalPetryka opened this issue Dec 20, 2024 · 3 comments
Labels
area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI tenet-performance Performance related issue untriaged New issue has not been triaged by the area owner

Comments

@MichalPetryka
Copy link
Contributor

MichalPetryka commented Dec 20, 2024

Description

While working on #109679 I've noticed that NativeAOT is unable to stack allocate delegate instances since it emits a call to a helper for the constructor which marks the variable as escaped.

cc @AndyAyersMS what would be the preferred solution for you here?

Configuration

n/a

Regression?

No.

Data

		public static void Alloc()
		{
			int a = 1;
			Unused(() => a);

			void Unused(Func<int> f) { }
		}

NativeAOT:

G_M6383_IG01:  ;; offset=0x0000
       push     rbx
       sub      rsp, 32
						;; size=5 bbWeight=1 PerfScore 1.25
G_M6383_IG02:  ;; offset=0x0005
       lea      rcx, [(reloc 0x40000000004203d0)]      ; DisasmoPlayground.Disasms.DelegateDisasm+<>c__DisplayClass3_0
       call     CORINFO_HELP_NEWSFAST
       mov      rbx, rax
       mov      dword ptr [rbx+0x08], 1
       lea      rcx, [(reloc 0x40000000004203f8)]      ; System.Func`1[int]
       call     CORINFO_HELP_NEWSFAST
       mov      rcx, rax
       mov      rdx, rbx
       call     CORINFO_HELP_READYTORUN_DELEGATE_CTOR
       nop      
						;; size=46 bbWeight=1 PerfScore 6.00
G_M6383_IG03:  ;; offset=0x0033
       add      rsp, 32
       pop      rbx
       ret      
						;; size=6 bbWeight=1 PerfScore 1.75

CoreCLR:

G_M6383_IG01:  ;; offset=0x0000
       sub      rsp, 40
						;; size=4 bbWeight=1 PerfScore 0.25
G_M6383_IG02:  ;; offset=0x0004
       mov      rcx, 0x7FF8358C0820      ; DisasmoPlayground.Disasms.DelegateDisasm+<>c__DisplayClass3_0
       call     CORINFO_HELP_NEWSFAST
       mov      dword ptr [rax+0x08], 1
						;; size=22 bbWeight=1 PerfScore 2.25
G_M6383_IG03:  ;; offset=0x001A
       add      rsp, 40
       ret      
						;; size=5 bbWeight=1 PerfScore 1.25

Analysis

The helper could either be special cased for escape analysis, NativeAOT could be migrated to work similar to CoreCLR or we could get a JIT intrinsic directly setting the delegate fields for known cases.

@MichalPetryka MichalPetryka added the tenet-performance Performance related issue label Dec 20, 2024
@dotnet-issue-labeler dotnet-issue-labeler bot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Dec 20, 2024
@dotnet-policy-service dotnet-policy-service bot added the untriaged New issue has not been triaged by the area owner label Dec 20, 2024
Copy link
Contributor

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

@MichalStrehovsky
Copy link
Member

UTC (the code generator backend in .NET Native) was taught the rules to inline the delegate creation sequence. I wouldn't be opposed to doing that for native AOT too.

Another option - that might be somewhat better since the rules on how to construct a delegate are arcane - could be to rewrite the delegate construction helper into IL (it's emitted as assembly right now) and give it to RyuJIT as a CORINFO_METHOD_STRUCT_ instead of CORINFO_LOOKUP i.e. similar to #110267. Then RyuJIT can inline the IL if it's worth it or keep it as a call. (The IL would be one of those "fake" methods the compiler generates whenever needed.)

It is a bit more work to do this in IL because first we'd need to make arbitrary ldftn/ldvirtftn compilable. We currently cut some corners around some ldvirtftn support because regular C# will exclusively use this as part of delegate creation sequence, but some ldvirtftn without a delegate constructor call following it will not compile with native AOT.

@AndyAyersMS
Copy link
Member

Any of these sounds fine to me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI tenet-performance Performance related issue untriaged New issue has not been triaged by the area owner
Projects
None yet
Development

No branches or pull requests

3 participants