Skip to content

Concrete type information does not propagate through struct fields #110290

@neon-sunset

Description

@neon-sunset

Description

It appears that JIT is not able to propagate concrete type information through struct fields typed as the type's abstract parent.
The example below matches the shape of stateless FSharpFunc<T1...Tn> instantiations by F#.

Given reduced repro:

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;

BenchmarkRunner.Run<Benchmarks>(args: args);

[ShortRunJob]
[DisassemblyDiagnoser]
public class Benchmarks {
    [Benchmark]
    public int Example() {
        var wrapper = default(Wrapper2<int, int>);
        wrapper.F1 = MyFuncImpl1.Instance;
        wrapper.F2 = MyFuncImpl2.Instance;

        var v1 = wrapper.F1.Invoke(42);
        var v2 = wrapper.F2.Invoke(v1);

        return v2 + 1;
    }
}

abstract class MyFunc<T, U> {
    public abstract U Invoke(T value);
}

sealed class MyFuncImpl1: MyFunc<int, int> {
    public static readonly MyFuncImpl1 Instance = new();
    public override int Invoke(int value) => value + 4;
}

sealed class MyFuncImpl2: MyFunc<int, int> {
    public static readonly MyFuncImpl2 Instance = new();
    public override int Invoke(int value) => value * 2;
}

struct Wrapper2<T, U> {
    public MyFunc<T, U> F1;
    public MyFunc<T, U> F2;
}

The Tier1-PGO compilation of Example() is

; Benchmarks.Dummy()
       push      rbx
       sub       rsp,20
       mov       rax,2907B8012E8
       mov       rcx,[rax]
       mov       rax,2907B8012F0
       mov       rbx,[rax]
       mov       rax,offset MT_MyFuncImpl1
       cmp       [rcx],rax
       jne       short M00_L01
       mov       edx,2E
       mov       rax,offset MT_MyFuncImpl2
       cmp       [rbx],rax
       jne       short M00_L02
       mov       eax,5C
M00_L00:
       inc       eax
       add       rsp,20
       pop       rbx
       ret
M00_L01:
       mov       edx,2A
       mov       rax,[rcx]
       mov       rax,[rax+40]
       call      qword ptr [rax+20]
       mov       edx,eax
M00_L02:
       mov       rcx,rbx
       mov       rax,[rbx]
       mov       rax,[rax+40]
       call      qword ptr [rax+20]
       jmp       short M00_L00
; Total bytes of code 111

It would be great if JIT could propagate the concrete type here through the field and not rely on DPGO to devirtualize the func types back. This also does not devirtualize on NativeAOT for more than single MyFunc implementation. Thanks!

Configuration

.NET SDK:
 Version:           9.0.100
 Commit:            59db016f11
 Workload version:  9.0.100-manifests.3068a692
 MSBuild version:   17.12.7+5b8665660

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.22631
 OS Platform: Windows
 RID:         win-x64
 Base Path:   C:\Program Files\dotnet\sdk\9.0.100\

Regression?

Not tested on .NET 8

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMItenet-performancePerformance related issue

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions