Closed
Description
Similiar to what #84661 does but for delegates now, e.g.:
static int Test(int a) => InvokeFunc(x => x + a);
static int InvokeFunc(Func<int, int> func) => func(100);
Codegen for Test(int a)
in Tier1 with Dynamic PGO enabled:
G_M38279_IG01:
push rdi
push rsi
sub rsp, 40
mov esi, ecx
G_M38279_IG02:
mov rcx, 0x7FF86A8D7078 ; Prog+<>c__DisplayClass1_0
call CORINFO_HELP_NEWSFAST
mov rdi, rax
mov dword ptr [rdi+08H], esi
mov rcx, 0x7FF86A8D72F8 ; System.Func`2[int,int]
call CORINFO_HELP_NEWSFAST
mov rsi, rax
lea rcx, bword ptr [rsi+08H]
mov rdx, rdi
call CORINFO_HELP_ASSIGN_REF
mov rax, 0x7FF86A8A8540 ; code for Prog+<>c__DisplayClass1_0:<Test>b__0(int):int:this
mov qword ptr [rsi+18H], rax
cmp qword ptr [rsi+18H], rax
jne SHORT G_M38279_IG05 ;; GDV check
G_M38279_IG03:
mov rax, gword ptr [rsi+08H]
mov eax, dword ptr [rax+08H]
add eax, 100 ;;;;;;;;;; <----- inlined lambda!! (100 + a)
G_M38279_IG04:
add rsp, 40
pop rsi
pop rdi
ret
G_M38279_IG05: ;;;; fallback for devirtualized lambda
mov rcx, gword ptr [rsi+08H]
mov edx, 100
call [rsi+18H]System.Func`2[int,int]:Invoke(int):int:this
jmp SHORT G_M38279_IG04
The delegate is correctly inlined here under a guard. Since we use a closure here, the delegate is always allocated at the callsite so we always know its exact type and the exact method target so we can optimize the GDV check and remove the software fallback:
mov rax, 0x7FF86A8A8540 ; code for Prog+<>c__DisplayClass1_0:<Test>b__0(int):int:this
mov qword ptr [rsi+18H], rax
cmp qword ptr [rsi+18H], rax
jne SHORT G_M38279_IG05
we write a value to a memory location and then compare that location with the same value. @jakobbotsch said the reason CSE didn't fold it is a missing FieldSeq*
that we can add during creating that GDV check (might require a new JIT-EE API to get a field handle of _methodPtr
field in Delegate
).
Follow up questions
- Once we remove the check - can we also remove the unnecessary delegate instance allocation?
- For static lambdas Roslyn currently emits a non-jit friendly pattern with a static mutable field, e.g. sharplab.io, so there is no way JIT can track the target - that can be improved on the Roslyn side - Discussion: Permit static lambda assignment to function pointers csharplang#6746 (reply in thread)