Skip to content

Add support for array interface devirtualization in R2R - #131265

Open
hez2010 wants to merge 24 commits into
dotnet:mainfrom
hez2010:r2r-array-devirt
Open

hez2010 wants to merge 24 commits into
dotnet:mainfrom
hez2010:r2r-array-devirt

Conversation

@hez2010

@hez2010 hez2010 commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Support array interface devirtualization in R2R.

When we see an interface method on System.Array, we check if it's an implicitly implemented array interface. For implicitly implemented array interfaces we get the actual implementation from SZArrayHelper.

Example:

public class Program
{
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    static IList<string> Get() => new string[] { "a", "b", "c" };

    static void Main(string[] args)
    {
        var list = Get();
        Console.WriteLine(list.Count);
        foreach (var item in list)
        {
            Console.WriteLine(item);
        }
    }
}

Before:

G_M24006_IG01:  ;; offset=0x0000
       push     rbp
       push     rbx
       sub      rsp, 40
       lea      rbp, [rsp+0x30]
                                                ;; size=11 bbWeight=1 PerfScore 2.75
G_M24006_IG02:  ;; offset=0x000B
       mov      ecx, 3
       call     [CORINFO_HELP_READYTORUN_NEWARR_1]
       mov      rbx, rax
       lea      rcx, bword ptr [rbx+0x10]
       mov      rdx, qword ptr [(reloc 0x420050)]      ; const ptr
       mov      rdx, gword ptr [rdx]
       call     [CORINFO_HELP_ASSIGN_REF]
       lea      rcx, bword ptr [rbx+0x18]
       mov      rdx, qword ptr [(reloc 0x420058)]      ; const ptr
       mov      rdx, gword ptr [rdx]
       call     [CORINFO_HELP_ASSIGN_REF]
       lea      rcx, bword ptr [rbx+0x20]
       mov      rdx, qword ptr [(reloc 0x420060)]      ; const ptr
       mov      rdx, gword ptr [rdx]
       call     [CORINFO_HELP_ASSIGN_REF]
       mov      rcx, rbx
       lea      r11, [(reloc 0x420118)]      ; function address
       call     [r11]System.Collections.Generic.ICollection`1[System.String]:get_Count():int:this
       mov      ecx, eax
       call     [System.Console:WriteLine(int)]
       mov      rcx, rbx
       lea      r11, [(reloc 0x420138)]      ; function address
       call     [r11]System.Collections.Generic.IEnumerable`1[System.String]:GetEnumerator():System.Collections.Generic.IEnumerator`1[System.String]:this
       mov      rbx, rax
       mov      gword ptr [rbp-0x10], rbx
                                                ;; size=115 bbWeight=1 PerfScore 38.00
G_M24006_IG03:  ;; offset=0x007E
       mov      rcx, rbx
       lea      r11, [(reloc 0x420150)]      ; function address
       call     [r11]System.Collections.IEnumerator:MoveNext():bool:this
       test     eax, eax
       je       SHORT G_M24006_IG05
                                                ;; size=17 bbWeight=8 PerfScore 40.00
G_M24006_IG04:  ;; offset=0x008F
       mov      rcx, rbx
       lea      r11, [(reloc 0x420160)]      ; function address
       call     [r11]System.Collections.Generic.IEnumerator`1[System.String]:get_Current():System.String:this
       mov      rcx, rax
       call     [System.Console:WriteLine(System.String)]
       jmp      SHORT G_M24006_IG03
                                                ;; size=24 bbWeight=4 PerfScore 36.00
G_M24006_IG05:  ;; offset=0x00A7
       mov      rcx, rbx
       lea      r11, [(reloc 0x420180)]      ; function address
       call     [r11]System.IDisposable:Dispose():this
       nop
                                                ;; size=14 bbWeight=1 PerfScore 4.00
G_M24006_IG06:  ;; offset=0x00B5
       add      rsp, 40
       pop      rbx
       pop      rbp
       ret
                                                ;; size=7 bbWeight=1 PerfScore 2.25
G_M24006_IG07:  ;; offset=0x00BC
       sub      rsp, 40
                                                ;; size=4 bbWeight=0 PerfScore 0.00
G_M24006_IG08:  ;; offset=0x00C0
       cmp      gword ptr [rbp-0x10], 0
       je       SHORT G_M24006_IG09
       mov      rcx, gword ptr [rbp-0x10]
       lea      r11, [(reloc 0x420180)]      ; function address
       call     [r11]System.IDisposable:Dispose():this
                                                ;; size=21 bbWeight=0 PerfScore 0.00
G_M24006_IG09:  ;; offset=0x00D5
       nop
                                                ;; size=1 bbWeight=0 PerfScore 0.00
G_M24006_IG10:  ;; offset=0x00D6
       add      rsp, 40
       ret
                                                ;; size=5 bbWeight=0 PerfScore 0.00

After:

G_M24006_IG01:  ;; offset=0x0000
       push     rbp
       push     rsi
       push     rbx
       sub      rsp, 32
       lea      rbp, [rsp+0x30]
                                                ;; size=12 bbWeight=1 PerfScore 3.75
G_M24006_IG02:  ;; offset=0x000C
       mov      ecx, 3
       call     [CORINFO_HELP_READYTORUN_NEWARR_1]
       mov      rbx, rax
       lea      rcx, bword ptr [rbx+0x10]
       mov      rdx, qword ptr [(reloc 0x420050)]      ; const ptr
       mov      rdx, gword ptr [rdx]
       call     [CORINFO_HELP_ASSIGN_REF]
       lea      rcx, bword ptr [rbx+0x18]
       mov      rdx, qword ptr [(reloc 0x420058)]      ; const ptr
       mov      rdx, gword ptr [rdx]
       call     [CORINFO_HELP_ASSIGN_REF]
       lea      rcx, bword ptr [rbx+0x20]
       mov      rdx, qword ptr [(reloc 0x420060)]      ; const ptr
       mov      rdx, gword ptr [rdx]
       call     [CORINFO_HELP_ASSIGN_REF]
       mov      ecx, 3
       call     [System.Console:WriteLine(int)]
       xor      esi, esi
                                                ;; size=87 bbWeight=1 PerfScore 29.50
G_M24006_IG03:  ;; offset=0x0063
       mov      ecx, esi
       mov      rcx, gword ptr [rbx+8*rcx+0x10]
       call     [System.Console:WriteLine(System.String)]
       inc      esi
       cmp      esi, 3
       jb       SHORT G_M24006_IG03
                                                ;; size=20 bbWeight=4 PerfScore 27.00
G_M24006_IG04:  ;; offset=0x0077
       add      rsp, 32
       pop      rbx
       pop      rsi
       pop      rbp
       ret
                                                ;; size=8 bbWeight=1 PerfScore 2.75

cc: @MichalStrehovsky @davidwrighton

Copilot AI lite review requested due to automatic review settings July 23, 2026 13:40
@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 Jul 23, 2026
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR updates the CoreCLR R2R compilation toolchain to allow devirtualization of interface calls on arrays by recognizing implicitly-implemented array interfaces and resolving the actual target methods on System.SZArrayHelper.

Changes:

  • Extend the well-known type system to include SZArrayHelper and key generic collection interfaces used by arrays.
  • Teach MetadataTypeSystemContext to resolve well-known types across multiple namespaces (e.g., System.Collections.Generic).
  • Enable R2R devirtualization for array interface calls by removing the previous R2R block and adding a SZArrayHelper-based resolution path in DevirtualizationManager.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/coreclr/tools/Common/TypeSystem/Common/WellKnownType.cs Adds well-known type enum entries for SZArrayHelper and array-relevant generic interfaces.
src/coreclr/tools/Common/TypeSystem/Common/TypeDesc.cs Classifies the new well-known types as Class/Interface for cached type flag computation.
src/coreclr/tools/Common/TypeSystem/Common/MetadataTypeSystemContext.cs Switches well-known type table to (Namespace, TypeName) to support non-System namespaces during system module initialization.
src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs Removes the R2R-time “array interface devirt not supported” early-fail, allowing devirtualization to proceed.
src/coreclr/tools/Common/Compiler/DevirtualizationManager.cs Adds array-interface recognition and maps eligible interface methods on System.Array to instantiated SZArrayHelper methods.

Comment thread src/coreclr/tools/Common/TypeSystem/Common/MetadataTypeSystemContext.cs Outdated
Comment thread src/coreclr/tools/Common/Compiler/DevirtualizationManager.cs Outdated
Copilot AI review requested due to automatic review settings July 23, 2026 13:59

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

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

Comments suppressed due to low confidence (2)

src/coreclr/tools/Common/Compiler/DevirtualizationManager.cs:93

  • GetActualImplementationForArrayGenericIListOrIReadOnlyListMethod assumes SZArrayHelper and the target method are always present, and will throw (TypeLoadException / NullReferenceException) if they aren't. Since well-known types are optionally present on some profiles, this should fail devirtualization gracefully instead of failing the compilation. This also fixes the typo "theT's" -> "T's" in the comment.
            MethodDesc genericImplementor = declMethod.Context.GetWellKnownType(WellKnownType.SZArrayHelper).GetMethod(declMethod.Name, null);
            Debug.Assert(genericImplementor != null);
            // OPTIMIZATION: For any method other than GetEnumerator(), we can safely substitute
            // "Object" for reference-type theT's. This causes fewer methods to be instantiated.
            if (genericImplementor.Name != "GetEnumerator"u8 && !typeParam.IsValueType)

src/coreclr/tools/Common/Compiler/DevirtualizationManager.cs:126

  • If GetActualImplementationForArrayGenericIListOrIReadOnlyListMethod can't locate SZArrayHelper (or the target method), the array-interface fast-path should set an explicit failure detail and return null, rather than returning a null MethodDesc and failing later.
                        return GetActualImplementationForArrayGenericIListOrIReadOnlyListMethod(declMethod, resultElemType);

Comment thread src/coreclr/tools/Common/Compiler/DevirtualizationManager.cs Outdated
Copilot AI review requested due to automatic review settings July 23, 2026 14:24
Comment thread src/coreclr/nativeaot/System.Private.CoreLib/src/System/Array.NativeAot.cs Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

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

Comments suppressed due to low confidence (1)

src/coreclr/tools/Common/TypeSystem/Common/MetadataTypeSystemContext.cs:55

  • MetadataTypeSystemContext.WellKnownTypeNames is a public API, and this PR changes its type from IEnumerable<string> to a tuple enumerable. That’s an API-breaking change for any out-of-repo consumers of the type system libraries.

To avoid breaking existing callers, keep WellKnownTypeNames with the original IEnumerable<string> shape and introduce a new property for (Namespace, TypeName) pairs (and update in-repo callers to use the new property).

        public static IEnumerable<(string Namespace, string TypeName)> WellKnownTypeNames => s_wellKnownTypeNames;

Comment thread src/coreclr/tools/dotnet-pgo/TypeRefTypeSystem/TypeRefTypeSystemContext.cs Outdated
Copilot AI review requested due to automatic review settings July 23, 2026 14:36

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

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

Comment thread src/coreclr/tools/Common/Compiler/DevirtualizationManager.cs Outdated
Copilot AI review requested due to automatic review settings July 23, 2026 14:58

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Comment thread src/coreclr/tools/Common/TypeSystem/Common/WellKnownType.cs Outdated
Comment thread src/coreclr/tools/Common/Compiler/DevirtualizationManager.cs Outdated
Copilot AI review requested due to automatic review settings July 24, 2026 00:34

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

It changes core R2R devirtualization behavior and JIT-interface context handling in a way that can affect codegen broadly, so it warrants final human review despite looking consistent with existing CoreCLR mechanisms.

Review details
  • Files reviewed: 4/4 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

@hez2010

hez2010 commented Sep 16, 2026

Copy link
Copy Markdown
Contributor Author

PTAL

@davidwrighton davidwrighton left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looks almost good to me. I'm curious if the new R2RTest infrastructure would let you write a test that validated that devirtualization happened for this case.

Comment thread src/coreclr/nativeaot/System.Private.CoreLib/src/System/Array.NativeAot.cs Outdated
Copilot AI review requested due to automatic review settings September 16, 2026 23:51

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

The new logic can classify multidimensional or pointer arrays as SZ arrays after type normalization and emit invalid direct helper calls.

Get a fresh assessment by requesting another Copilot review.

Review details
  • Files reviewed: 3/3 changed files
  • Comments generated: 1
  • Review effort level: Lite

Copilot AI review requested due to automatic review settings September 22, 2026 15:25

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

Variant array casts may resolve to an SZArrayHelper instantiation without a compiled body or dependency.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 1 High severity

Open (1)
Resolved since last review (1)

jkotas pushed a commit that referenced this pull request Sep 22, 2026
Noticed while working on #131265.

We were not inlining `GetEnumerator` here under NativeAOT, leading to
foreach over array interfaces not able to be inlined even after
devirtualization.
@hez2010

hez2010 commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor Author

I'm curious if the new R2RTest infrastructure would let you write a test that validated that devirtualization happened for this case.

I didn't know about the new R2RTest infrastructure, could you elaborate a bit? (searching R2RTest infra in issues and prs yielded nothing related for me) I have verified this locally and the devirtualization is happening.

@jkoritzinsky

Copy link
Copy Markdown
Member

We have some tests under https://github.com/dotnet/runtime/tree/main/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests that let us programmatically validate ReadyToRun outputs. We've started adding tests for some cases.

Copilot AI review requested due to automatic review settings September 24, 2026 11:50
@hez2010

hez2010 commented Sep 24, 2026

Copy link
Copy Markdown
Contributor Author

I added the tests for it. PTAL.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🔵 Needs a closer look

The changes require final human review because they are too complex or risky for automated approval.

Review effort: Lite
Findings: None

Resolved since last review (1)

Copilot AI review requested due to automatic review settings September 24, 2026 11:59

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🔵 Needs a closer look

The changes affect R2R devirtualization and fixup resolution across multiple compiler components.

Review effort: Lite
Findings: None

This branch has not been deployed

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

Labels

area-ReadyToRun linkable-framework Issues associated with delivering a linker friendly framework

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants