Skip to content

Conversation

@AndyAyersMS
Copy link
Member

The instantiated methods should use the parameter type of the interface, not the element type of the array.

Fixes #120270.

The instantiated methods should use the parameter type of the interface,
not the element type of the array.

Fixes dotnet#120270.
Copilot AI review requested due to automatic review settings October 1, 2025 22:42
@github-actions github-actions bot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Oct 1, 2025
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Fixes an issue with array interface devirtualization where the JIT was incorrectly using the array element type instead of the interface parameter type when instantiating methods. This caused failures during array interface method calls when the array element type was canonical but the interface type was not.

Key Changes:

  • Updated devirtualization logic to use interface parameter type instead of array element type
  • Added regression test to verify the fix works correctly
  • Removed unnecessary canonical subtype check since interface type is guaranteed to not be canonical

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
src/coreclr/vm/jitinterface.cpp Fixed devirtualization logic to use interface parameter type instead of array element type
src/tests/JIT/Regression/JitBlue/Runtime_120270/Runtime_120270.cs Added regression test that reproduces the original issue
src/tests/JIT/Regression/JitBlue/Runtime_120270/Runtime_120270.csproj Project file for the regression test

@AndyAyersMS
Copy link
Member Author

@davidwrighton PTAL
cc @dotnet/jit-contrib

@hez2010
Copy link
Contributor

hez2010 commented Oct 2, 2025

@AndyAyersMS
Copy link
Member Author

/azp run runtime-coreclr outerloop, runtime-coreclr pgo

@azure-pipelines
Copy link

Azure Pipelines successfully started running 2 pipeline(s).

@AndyAyersMS
Copy link
Member Author

This is going to regress deabstraction for enumeration of arrays of reference types, eg System.Collections.IterateForEach<String>.IEnumerable(Size: 512)

Method Job Toolchain Size Mean Error StdDev Median Min Max Ratio RatioSD Allocated Alloc Ratio
IEnumerable Job-GCLHZJ PR 512 981.0 ns 4.03 ns 3.37 ns 981.0 ns 976.4 ns 986.4 ns 5.96 0.05 32 B NA
IEnumerable Job-XFHGQV Base 512 168.6 ns 2.03 ns 1.80 ns 167.7 ns 167.0 ns 173.2 ns 1.02 0.01 - NA
IEnumerable Job-ZIJYVN net10.0-rc1 512 164.6 ns 1.41 ns 1.25 ns 163.9 ns 163.4 ns 166.9 ns 1.00 0.01 - NA

Seems like to handle this we now need to profile both the object type and the interface type as a pair, and GDV for both, or something like that.

Or maybe just GDV for the object type and then add a runtime check that the interface type is the same as the array element type?

Need to look at this some more but seems possible there's no simple way to avoid taking this hit.

@AndyAyersMS
Copy link
Member Author

@davidwrighton for ref types wouldn't normal variance rules apply? So using the element type there is ok?

@davidwrighton
Copy link
Member

davidwrighton commented Oct 2, 2025

@AndyAyersMS unfortunately... a string[] will expose both IList<string> and IList<object> and it will actually call different methods under the covers here and expose different enumerators.

@AndyAyersMS
Copy link
Member Author

Here's an example showing us switching ref array enumerator types after optimizing:

class Base {}
class Derived : Base {}
       
class Problem
{              
    public static void Test()
    {
        for (int i = 0; i < 100_000; i++)
        {
            Derived[] d = [new Derived()];
            IEnumerable<Base> e = d;
            IEnumerator<Base> en = e.GetEnumerator();
            if (i % 20_000 == 0)
            {
                Console.WriteLine(en.GetType());
            }
        }
    }
}

Running this prints

System.SZGenericArrayEnumerator`1[Base]
System.SZGenericArrayEnumerator`1[Derived]
System.SZGenericArrayEnumerator`1[Derived]
System.SZGenericArrayEnumerator`1[Derived]
System.SZGenericArrayEnumerator`1[Derived]

with the switch happening after OSR kicks in.

@AndyAyersMS
Copy link
Member Author

@davidwrighton updated to block devirt if interface type is _Canon.

@AndyAyersMS AndyAyersMS merged commit d0bdc78 into dotnet:main Oct 3, 2025
102 of 104 checks passed
@AndyAyersMS
Copy link
Member Author

/backport to release/10.0

@github-actions
Copy link
Contributor

github-actions bot commented Oct 3, 2025

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

Labels

area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

.NET 10 RC1 - System.EntryPointNotFoundException with enumerator created by Cast<T>

3 participants