Skip to content

Apply existing constrained call target refinements to static abstract interface methods - #135169

Open
NinoFloris wants to merge 2 commits into
dotnet:mainfrom
NinoFloris:fix/static-abstract-interface-target-opts
Open

NinoFloris wants to merge 2 commits into
dotnet:mainfrom
NinoFloris:fix/static-abstract-interface-target-opts

Conversation

@NinoFloris

Copy link
Copy Markdown
Contributor

Existing constrained call resolution refinements weren’t applied to static abstract interface methods: static calls were handled by an early-return path before reaching them. This moves that path after the refinements and extends the existing logic to handle static members where necessary.

This fixes #135168, and a few other deficiencies (see regression cases).

Tested on macOS arm64 Release, tiered compilation disabled. Before the change, all five static calls use a generic-dictionary lookup and an indirect call. Afterward, each implementation is inlined, with no lookup or call remaining.

Call Case Before After Inlined body
CallUnique Original FuncImpl case 100 bytes 20 bytes mov w0, w2 — return the tuple's integer field
CallExactGeneric Exact generic struct, ExactGenericFunc<int> 84 bytes 20 bytes add w0, w2, #5
CallDefault Default interface implementation 84 bytes 20 bytes mov w0, #7
CallCovariantUnique Unique covariant interface implementation 60 bytes 28 bytes Load the constant typeof(string) reference
CallGenericExact Generic method with exact int argument 84 bytes 28 bytes Load the constant typeof(int) reference

Address values shown as 0xD1FFAB1E are placeholders emitted by JitDisasmDiffable.

CallUnique: 100 → 20 bytes

Before:

G_M000_IG01:
            stp     fp, lr, [sp, #-0x30]!
            stp     x19, x20, [sp, #0x20]
            mov     fp, sp
            str     x0, [fp, #0x18]
            mov     x19, x1
            mov     w20, w2

G_M000_IG02:
            ldr     x1, [x0, #0x18]
            ldr     x2, [x1, #0x18]
            cbz     x2, G_M000_IG05

G_M000_IG03:
            mov     x0, x19
            mov     w1, w20
            blr     x2

G_M000_IG04:
            ldp     x19, x20, [sp, #0x20]
            ldp     fp, lr, [sp], #0x30
            ret     lr

G_M000_IG05:
            movz    x1, #0xD1FFAB1E
            movk    x1, #0xD1FFAB1E LSL #16
            movk    x1, #1 LSL #32
            movz    x2, #0xD1FFAB1E
            movk    x2, #0xD1FFAB1E LSL #16
            movk    x2, #1 LSL #32
            ldr     x2, [x2]
            blr     x2      // code for CORINFO_HELP_RUNTIMEHANDLE_METHOD
            mov     x2, x0
            b       G_M000_IG03

After:

G_M000_IG01:
            stp     fp, lr, [sp, #-0x10]!
            mov     fp, sp

G_M000_IG02:
            mov     w0, w2

G_M000_IG03:
            ldp     fp, lr, [sp], #0x10
            ret     lr
CallExactGeneric: 84 → 20 bytes

Before:

G_M000_IG01:
            stp     fp, lr, [sp, #-0x30]!
            stp     x19, x20, [sp, #0x20]
            mov     fp, sp
            str     x0, [fp, #0x18]
            mov     x19, x1
            mov     w20, w2

G_M000_IG02:
            ldr     x1, [x0, #0x18]
            ldr     x2, [x1, #0x18]
            cbz     x2, G_M000_IG05

G_M000_IG03:
            mov     x0, x19
            mov     w1, w20
            blr     x2

G_M000_IG04:
            ldp     x19, x20, [sp, #0x20]
            ldp     fp, lr, [sp], #0x30
            ret     lr

G_M000_IG05:
            movz    x1, #0xD1FFAB1E
            movk    x1, #0xD1FFAB1E LSL #16
            movk    x1, #1 LSL #32
            bl      CORINFO_HELP_RUNTIMEHANDLE_METHOD
            mov     x2, x0
            b       G_M000_IG03

After:

G_M000_IG01:
            stp     fp, lr, [sp, #-0x10]!
            mov     fp, sp

G_M000_IG02:
            add     w0, w2, #5

G_M000_IG03:
            ldp     fp, lr, [sp], #0x10
            ret     lr
CallDefault: 84 → 20 bytes

Before:

G_M000_IG01:
            stp     fp, lr, [sp, #-0x30]!
            stp     x19, x20, [sp, #0x20]
            mov     fp, sp
            str     x0, [fp, #0x18]
            mov     x19, x1
            mov     w20, w2

G_M000_IG02:
            ldr     x1, [x0, #0x18]
            ldr     x2, [x1, #0x18]
            cbz     x2, G_M000_IG05

G_M000_IG03:
            mov     x0, x19
            mov     w1, w20
            blr     x2

G_M000_IG04:
            ldp     x19, x20, [sp, #0x20]
            ldp     fp, lr, [sp], #0x30
            ret     lr

G_M000_IG05:
            movz    x1, #0xD1FFAB1E
            movk    x1, #0xD1FFAB1E LSL #16
            movk    x1, #1 LSL #32
            bl      CORINFO_HELP_RUNTIMEHANDLE_METHOD
            mov     x2, x0
            b       G_M000_IG03

After:

G_M000_IG01:
            stp     fp, lr, [sp, #-0x10]!
            mov     fp, sp

G_M000_IG02:
            mov     w0, #7

G_M000_IG03:
            ldp     fp, lr, [sp], #0x10
            ret     lr
CallCovariantUnique: 60 → 28 bytes

Before:

G_M000_IG01:
            stp     fp, lr, [sp, #-0x20]!
            mov     fp, sp
            str     x0, [fp, #0x18]

G_M000_IG02:
            ldr     x1, [x0, #0x18]
            ldr     x2, [x1, #0x18]
            cbz     x2, G_M000_IG05

G_M000_IG03:
            blr     x2

G_M000_IG04:
            ldp     fp, lr, [sp], #0x20
            ret     lr

G_M000_IG05:
            movz    x1, #0xD1FFAB1E
            movk    x1, #0xD1FFAB1E LSL #16
            movk    x1, #1 LSL #32
            bl      CORINFO_HELP_RUNTIMEHANDLE_METHOD
            mov     x2, x0
            b       G_M000_IG03

After:

G_M000_IG01:
            stp     fp, lr, [sp, #-0x10]!
            mov     fp, sp

G_M000_IG02:
            movz    x0, #0xD1FFAB1E
            movk    x0, #0xD1FFAB1E LSL #16
            movk    x0, #1 LSL #32

G_M000_IG03:
            ldp     fp, lr, [sp], #0x10
            ret     lr
CallGenericExact: 84 → 28 bytes

Before:

G_M000_IG01:
            stp     fp, lr, [sp, #-0x30]!
            stp     x19, x20, [sp, #0x20]
            mov     fp, sp
            str     x0, [fp, #0x18]
            mov     x19, x1
            mov     w20, w2

G_M000_IG02:
            ldr     x1, [x0, #0x18]
            ldr     x2, [x1, #0x20]
            cbz     x2, G_M000_IG05

G_M000_IG03:
            mov     x0, x19
            mov     w1, w20
            blr     x2

G_M000_IG04:
            ldp     x19, x20, [sp, #0x20]
            ldp     fp, lr, [sp], #0x30
            ret     lr

G_M000_IG05:
            movz    x1, #0xD1FFAB1E
            movk    x1, #0xD1FFAB1E LSL #16
            movk    x1, #1 LSL #32
            bl      CORINFO_HELP_RUNTIMEHANDLE_METHOD
            mov     x2, x0
            b       G_M000_IG03

After:

G_M000_IG01:
            stp     fp, lr, [sp, #-0x10]!
            mov     fp, sp

G_M000_IG02:
            movz    x0, #0xD1FFAB1E
            movk    x0, #0xD1FFAB1E LSL #16
            movk    x0, #1 LSL #32

G_M000_IG03:
            ldp     fp, lr, [sp], #0x10
            ret     lr

@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 3, 2026
@dotnet-policy-service dotnet-policy-service Bot added the community-contribution Indicates that the PR has been added by a community member label Oct 3, 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.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

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

@NinoFloris

Copy link
Copy Markdown
Contributor Author

Failure is #134825

@MichalStrehovsky MichalStrehovsky added area-TypeSystem-coreclr and removed area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI labels Oct 5, 2026
… interface methods

Extend the existing instance target-resolution strategy to constrained static interface calls on value types. Preserve instance handling, static defaults, and reference-type hierarchy resolution without changing dictionary lookup or call generation.

Add functional coverage for shared receivers, generic methods, defaults, variance, and delegates, plus codegen checks for five cases that now inline, including the FuncImpl scenario.
@NinoFloris
NinoFloris force-pushed the fix/static-abstract-interface-target-opts branch from 9729caa to a75b0b1 Compare October 30, 2026 13:15
@NinoFloris

NinoFloris commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor Author

Added another commit allowing target refinement for static abstract calls on reference types. Instance calls retain the existing restriction to value types. Static abstract calls now use the same target refinements for class targets.

The reason this is safe is that static abstract dispatch depends on the constrained type argument, not an object’s runtime type.

This is one part of improving static abstract performance in shared generics when the implementing type family is known (e.g. Node<T>, instantiated with string). We can identify the interface implementation even though the generic argument causes the code to be shared.

@NinoFloris

Copy link
Copy Markdown
Contributor Author

@MihuBot

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-TypeSystem-coreclr community-contribution Indicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Try to resolve exact targets for constrained static interface calls

2 participants