Skip to content

NativeAOT: GVM analysis only runs on canonical form for shared instantiations #130752

Description

@hez2010

The whole program analysis of GVMs currently assumes the call will be lazy and will go through the type loader. If we enable devirtualization for shared generic virtual methods, the following test will fail:

    class TestGvmDependencies
    {
        class Atom { }

        class Foo
        {
            public virtual object Frob<T>()
            {
                return new T[0, 0];
            }
        }

        class Bar : Foo
        {
            public override object Frob<T>()
            {
                return new T[0, 0, 0];
            }
        }

        public static void Run()
        {
            {
                Foo x = new Foo();
                x.Frob<Atom>();
            }

            {
                Foo x = new Bar();
                x.Frob<Atom>();
            }
        }
    }

We don't expect Atom[,] to exist during scanning because GVM analysis only ever sees Frob<__Canon> (we deliberately ignore the Frob<Atom> call and consider it just Frob<__Canon>). We do this for two reasons - compile speed, and size. GVMs expand very aggressively and not precomputing all the types that we could build lazily saves us both.

We could in theory have a mode that does the full analysis and analyzes Frob<Atom> that then forces both Atom[,] and Atom[,,] to exists, but we don't have it yet.

The GVM devirt under NativeAOT has been limited to struct instantiations only for now. We could allow reference types if the other analysis mode is added.

This is one of the things where RyuJIT-as-IL-scanner would help because then we would do this devirt during scanning already and GVM analysis never gets in the picture (it's not considered a GVM call in the first place).

Originally posted by @MichalStrehovsky in #130202 (comment).

Activity

  1. added
    area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI
    on Jul 15, 2026
  2. dotnet-policy-service commented on Jul 15, 2026

    @dotnet-policy-service
    Contributor

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

  3. MichalStrehovsky commented on Jul 15, 2026

    @MichalStrehovsky
    Member

    The problem is not that the scanner doesn't scan it, but the GVM analysis (that runs irrespective of IL scanner since we need the analysis for debug builds too - it predicts all generic virtual method bodies we need, given the types that are allocated) is running only on __Canon. This was deliberate to limit compile time and sizes. We could have a mode where we analyze concrete things, but it would have to be opt in. That's one way the devirtualization could be solved. It will deoptimize the other things. It's not my preference. We actually used to do that before and it was making Avalonia 50% bigger than needed (dotnet/runtimelab#537).

    The other option is using RyuJIT-as-a-scanner so that this is not even considered a GVM call. That's tracked in #83021.

  4. changed the title [-]NativeAOT: IL scanner does not scan shared instantiaiton of GVMs[/-] [+]NativeAOT: GVM analysis only runs on canonical form for shared instantiations[/+] on Jul 15, 2026
  5. hez2010 commented on Jul 15, 2026

    @hez2010
    ContributorAuthor

    The problem is not that the scanner doesn't scan it, but the GVM analysis (that runs irrespective of IL scanner since we need the analysis for debug builds too - it predicts all generic virtual method bodies we need, given the types that are allocated) is running only on __Canon.

    Thanks for clarifying. I have updated the title of issue.

  6. added this to the Future milestone on Jul 15, 2026
  7. dotnet-policy-service commented on Jul 15, 2026

    @dotnet-policy-service
    Contributor

    Tagging subscribers to this area: @agocke, @dotnet/ilc-contrib
    See info in area-owners.md if you want to be subscribed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    • Status
      No status

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions