Skip to content

Conversation

@sbomer
Copy link
Member

@sbomer sbomer commented Nov 27, 2025

When RyuJit inlines a generic callee into a callsite where some of the generic arguments are concrete, the code generation may require concrete dependencies of the inlinee. We need to ensure that the scanner tracks such dependencies statically so that they are available for codegen.

For example:

using System;
using System.Runtime.CompilerServices;

class Program
{
    static void Main()  
    {
        var r = typeof(Program).GetMethod(nameof(Caller)).MakeGenericMethod(GetStringType()).Invoke(null, []);
    }

    static Type GetStringType() => typeof(string);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    static string Callee<T, U>() => new Container<T>().ToString();

    public static string Caller<T>() => Callee<object, T>();
}

class Container<T>;

Here the concrete instantiation Caller<string> is not discovered statically - but when Callee<object, T>() is inlined into the canonical code for Caller<__Canon>, this results in a concrete reference to Container<object> from Caller<__Canon>, so we need to keep any generic dictionary dependencies of Callee<object, T>.

This change adds additional tracking of open generic methods to ensure that any concrete dependencies are preserved during scanning. It adds tracking similar to what's done by ShadowConcreteMethodNode - now there's a new ShadowNonConcreteMethodNode that tracks non-concrete (not fully instantiated) generics. There are three entry points where we needed additional tracking for open generics:

  • When reflecting over a generic method
  • When encountering a GVM (this was the case in the original issue)
  • When we see a direct call to a generic method

Now when ShadowNonConcreteMethodNode is used to instantiate generic dependencies of runtime-determined nodes, the resulting instantiation may not be concrete. We handle this by attempting the instantiation, and if the result is concrete we track the dependency as normal. If the result is not concrete, then we either track it as another ShadowNonConcreteMethodNode (if the result is a generic method) or ignore it.

Fixes #120847

@dotnet-policy-service
Copy link
Contributor

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

@sbomer sbomer force-pushed the vtableScannerFix branch 2 times, most recently from d40b283 to 5d9b2bc Compare December 10, 2025 00:51
To fix case without method generic parameters
No longer needed since the conversion of
generic unboxing thunks to the underlying
target method no longer happens here.
@sbomer sbomer changed the title [WIP] Create MethodGenericDictionary for calls to GVMs ILC: track concrete dependencies of open generic methods Dec 16, 2025
@sbomer sbomer marked this pull request as ready for review December 16, 2025 19:48
Copilot AI review requested due to automatic review settings December 16, 2025 19:48
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

This PR enhances the ILC (IL Compiler) to track concrete dependencies of open generic methods to prevent compilation failures when RyuJit inlines generic callees into callsites with concrete generic arguments. The key change is expanding the scope of dependency tracking from only concrete generic methods to include partially instantiated (open) generic methods that may still have concrete dependencies.

  • Renamed ShadowConcreteMethodNode to ShadowGenericMethodNode to reflect its expanded purpose
  • Added tracking for open generic methods in three critical entry points: reflection access, generic virtual method (GVM) calls, and direct method calls
  • Enhanced ReadyToRunGenericHelperNode.InstantiateDependencies to handle non-concrete instantiations by attempting substitution and tracking shadow generic methods when results remain canonical

Reviewed changes

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

Show a summary per file
File Description
ShadowGenericMethodNode.cs Renamed from ShadowConcreteMethodNode.cs; removed assertion that restricted usage to concrete methods only, allowing tracking of open generic methods
ReadyToRunGenericHelperNode.cs Added logic to handle non-concrete instantiations by checking substitution results and tracking shadow generic methods when instantiations remain canonical
ReflectionInvokeMapNode.cs Added tracking of shadow generic methods for shared generic instantiations accessed via reflection
GenericVirtualMethodImplNode.cs Added shadow generic method dependency tracking for generic virtual method implementations
GenericDictionaryNode.cs Updated references from ShadowConcreteMethod to ShadowGenericMethod
NodeFactory.cs Renamed method and hashtable from ShadowConcreteMethod to ShadowGenericMethod; updated CanonicalEntrypoint method
ILImporter.Scanner.cs Changed direct call tracking to use ShadowGenericMethod instead of CanonicalEntrypoint
MetadataManager.cs Updated type cast from ShadowConcreteMethodNode to ShadowGenericMethodNode
ILScanner.cs Updated type cast from ShadowConcreteMethodNode to ShadowGenericMethodNode
RyuJitCompilation.cs Updated type cast from ShadowConcreteMethodNode to ShadowGenericMethodNode
ILCompiler.Compiler.csproj Updated file reference from ShadowConcreteMethodNode.cs to ShadowGenericMethodNode.cs
ILCompiler.ReadyToRun.csproj Updated file reference from ShadowConcreteMethodNode.cs to ShadowGenericMethodNode.cs
Generics.cs Added five comprehensive test cases covering GVM inlining with different inheritance patterns and reflection scenarios

…nalysis/GenericVirtualMethodImplNode.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
- Split ShadowGenericMethodNode into:
  - common base class ShadowMethodNode
  - existing ShadowConcreteMethodNode
  - new ShadowNonConcreteMethodNode
  with specific asserts in the derived classes
- fix comment
- pass isConcreteInstantiation through
  InstantiateDependencies and GetTarget
- limit creation of ShadowNonConcreteMethodNode
  to where AcquiresInstMethodTableFromThis is true
- move instantiation logic for non-concrete instantiations
  into GetTarget implementations
InstantiateDependencies here could be called for non-concrete
instantiations even before these changes.
And limit direct call shadow nodes
We already create concrete nodes inside CanonicalEntrypoint
Copy link
Member

@MichalStrehovsky MichalStrehovsky left a comment

Choose a reason for hiding this comment

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

Looks good otherwise! I'll merge main into this so I can run size measurements over in rt-sz, just in case something unexpected pops up.

@MichalStrehovsky
Copy link
Member

/azp run runtime-nativeaot-outerloop

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@MichalStrehovsky
Copy link
Member

I'll merge main into this so I can run size measurements over in rt-sz, just in case something unexpected pops up.

Ah, I misinterpreted what rt-sz was complaining about. You already merged main before, so we had #122227 in this brach, but the SDK produced from dotnet/dotnet still doesn't have this, so rt-sz is failing with EXEC : error : Unknown stack trace data: --scanreflection. Merging main now obviously didn't resolve this. We need flow from runtime into dotnet/dotnet to resume :(.

sbomer added 2 commits January 2, 2026 10:01
- Remove dead code
- Remove redundant check
- Improve comment
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

IL scanner VTable error

2 participants