Skip to content
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
3dfcffa
Add known methods that may be unreferenced into the MutableModule.
jtschuster Nov 16, 2025
13f7d1d
Only add types and methods outside of the version bubble, still can't…
jtschuster Nov 16, 2025
d638752
Fix extra changes
jtschuster Nov 16, 2025
80c0a5b
Fix EntityHandle spacing and use HashSet instead of ImmutableHashSet
jtschuster Nov 18, 2025
660f62c
Merge branch 'main' of https://github.com/dotnet/runtime into Mutable…
jtschuster Nov 18, 2025
9d35db6
Add async helper methods to ReadyToRunHelpers
jtschuster Nov 21, 2025
a9eff16
Merge branch 'main' of https://github.com/dotnet/runtime into Mutable…
jtschuster Nov 25, 2025
94d8de1
Fix build issues
jtschuster Nov 25, 2025
430531c
Trigger recompilation for async thunks that don't have references to …
jtschuster Dec 2, 2025
f1082d5
Revert changes to helper method enums
jtschuster Dec 2, 2025
003efb0
Revert files to updated upstream version
jtschuster Dec 2, 2025
0d7a521
Use TypeSystemEntity to ensure references to types and methods
jtschuster Dec 2, 2025
4833400
Remove unused file
jtschuster Dec 2, 2025
305958a
Revert extraneous changes
jtschuster Dec 2, 2025
ccc8f9f
Fix project file, no need to use out param for thunkhelpers
jtschuster Dec 2, 2025
2fa0a48
Fix build break
jtschuster Dec 2, 2025
9f42a55
Merge branch 'main' of https://github.com/dotnet/runtime into Mutable…
jtschuster Dec 2, 2025
7199f97
Replace AsyncHelpers enum with KnownILStubReferences
jtschuster Dec 3, 2025
88a6d5d
Missed a couple references to old methods.
jtschuster Dec 3, 2025
5a5dc81
Add references to mutable module before compilation, don't allow asyn…
jtschuster Dec 5, 2025
90e1a52
Remove unnecessary changes
jtschuster Dec 6, 2025
3297cc8
Typo: Pop should be Push
jtschuster Dec 6, 2025
e708947
Emit correct ldloc/ldloca for task/valuetask locals
jtschuster Dec 6, 2025
61cb9cc
Merge branch 'main' into MutableModule
jtschuster Dec 8, 2025
1112136
Update log message
jtschuster Dec 8, 2025
70a88f6
Use ILReader to find the references in thunks
jtschuster Dec 10, 2025
b387f78
Remove KnownReferences enum
jtschuster Dec 11, 2025
460bb27
Only locals and instatiations can use ELEMENT_TYPE_X
jtschuster Dec 11, 2025
4c04d16
Account for all il opcodes, add tokens for all TypeDescs
jtschuster Dec 12, 2025
456cfce
Move ILStubReferences.cs
jtschuster Dec 13, 2025
4d3d68f
Fix path issue
jtschuster Dec 15, 2025
94b763c
Merge branch 'main' into MutableModule
jtschuster Dec 16, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/coreclr/tools/Common/Compiler/AsyncMethodVariant.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,5 +78,10 @@ public static bool IsAsyncVariant(this MethodDesc method)
{
return method.GetTypicalMethodDefinition() is AsyncMethodVariant;
}

public static bool IsAsyncThunk(this MethodDesc method)
{
return method.IsAsyncVariant() ^ method.IsAsync;
}
}
}
118 changes: 118 additions & 0 deletions src/coreclr/tools/Common/TypeSystem/IL/ILStubReferences.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.


using System;
using System.Collections.Generic;
using System.Diagnostics;
using ILCompiler;
using Internal.TypeSystem;
using Internal.TypeSystem.Ecma;

namespace Internal.IL
{
public class ILStubReferences
{
/// <summary>
/// Extracts all method and type references from the IL of a method.
/// This is used to find references in synthetic IL (like async thunks) that may need
/// to be added to the mutable module.
/// </summary>
public static List<TypeSystemEntity> GetNecessaryReferencesFromIL(MethodIL methodIL)
Comment thread
jtschuster marked this conversation as resolved.
Outdated
{
var references = new List<TypeSystemEntity>();
byte[] ilBytes = methodIL.GetILBytes();
ILReader reader = new ILReader(ilBytes);

while (reader.HasNext)
{
ILOpcode opcode = reader.ReadILOpcode();

switch (opcode)
{
case ILOpcode.call:
case ILOpcode.callvirt:
case ILOpcode.newobj:
case ILOpcode.ldftn:
case ILOpcode.ldvirtftn:
case ILOpcode.jmp:
{
int token = reader.ReadILToken();
object obj = methodIL.GetObject(token, NotFoundBehavior.ReturnNull);
if (obj is MethodDesc method)
{
references.Add(method);
}
break;
}

case ILOpcode.newarr:
case ILOpcode.castclass:
case ILOpcode.isinst:
case ILOpcode.box:
case ILOpcode.unbox:
case ILOpcode.unbox_any:
case ILOpcode.ldobj:
case ILOpcode.stobj:
case ILOpcode.cpobj:
case ILOpcode.initobj:
case ILOpcode.ldelem:
case ILOpcode.stelem:
case ILOpcode.ldelema:
case ILOpcode.constrained:
case ILOpcode.sizeof_:
case ILOpcode.mkrefany:
case ILOpcode.refanyval:
{
int token = reader.ReadILToken();
object obj = methodIL.GetObject(token, NotFoundBehavior.ReturnNull);
if (obj is TypeDesc type)
{
references.Add(type);
}
break;
}

case ILOpcode.ldtoken:
{
int token = reader.ReadILToken();
object obj = methodIL.GetObject(token, NotFoundBehavior.ReturnNull);
if (obj is MethodDesc method)
{
references.Add(method);
}
else if (obj is TypeDesc type)
{
references.Add(type);
}
break;
}

default:
reader.Skip(opcode);
break;
}
}

foreach (var region in methodIL.GetExceptionRegions())
{
if (region.Kind == ILExceptionRegionKind.Catch && region.ClassToken != 0)
{
object obj = methodIL.GetObject(region.ClassToken, NotFoundBehavior.ReturnNull);
if (obj is TypeDesc type)
references.Add(type);
}
}

foreach (var local in methodIL.GetLocals())
{
TypeDesc type = local.Type;
// These types can be represented by ELEMENT_TYPE values and don't need references.
if (!type.IsPrimitive && !type.IsVoid && !type.IsObject && !type.IsString && !type.IsTypedReference)
references.Add(type);
}

return references;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,12 @@ public bool CanInline(MethodDesc caller, MethodDesc callee)
}
}

if (callee.IsAsyncThunk())
{
// Async thunks require special handling in the compiler and should not be inlined
return false;
}

_nodeFactory.DetectGenericCycles(caller, callee);

return NodeFactory.CompilationModuleGroup.CanInline(caller, callee);
Expand Down Expand Up @@ -694,6 +700,50 @@ protected override void ComputeDependencyNodeDependencies(List<DependencyNodeCor
CorInfoImpl.IsMethodCompilable(this, methodCodeNodeNeedingCode.Method))
_methodsWhichNeedMutableILBodies.Add(ecmaMethod);
}
if (method.IsAsyncThunk())
{
// The synthetic async thunks require references to methods/types that may not have existing methodRef entries in the version bubble.
// These references need to be added to the mutable module if they don't exist.
// Extract the required references by reading the IL of the thunk method.
MethodIL methodIL = GetMethodIL(method);
if (methodIL is null)
continue;

_nodeFactory.ManifestMetadataTable._mutableModule.ModuleThatIsCurrentlyTheSourceOfNewReferences = ((EcmaMethod)method.GetTypicalMethodDefinition()).Module;
try
{
foreach (var entity in ILStubReferences.GetNecessaryReferencesFromIL(methodIL))
{
switch (entity)
{
case MethodDesc md:
if (md.IsAsyncVariant())
{
Debug.Assert(((CompilerTypeSystemContext)md.Context).GetTargetOfAsyncVariantMethod(md.GetTypicalMethodDefinition()) == method);
Debug.Assert(!_nodeFactory.Resolver.GetModuleTokenForMethod(method, allowDynamicallyCreatedReference: false, throwIfNotFound: true).IsNull);
}
else if (_nodeFactory.Resolver.GetModuleTokenForMethod(md, allowDynamicallyCreatedReference: true, throwIfNotFound: false).IsNull)
{
_nodeFactory.ManifestMetadataTable._mutableModule.TryGetEntityHandle(md);
Debug.Assert(!_nodeFactory.Resolver.GetModuleTokenForMethod(md, allowDynamicallyCreatedReference: true, throwIfNotFound: true).IsNull);
}
break;
case EcmaType td:
Comment thread
jtschuster marked this conversation as resolved.
Outdated
if (_nodeFactory.Resolver.GetModuleTokenForType(td, allowDynamicallyCreatedReference: true, throwIfNotFound: false).IsNull)
{
_nodeFactory.ManifestMetadataTable._mutableModule.TryGetEntityHandle(td);
}
Debug.Assert(!_nodeFactory.Resolver.GetModuleTokenForType(td, allowDynamicallyCreatedReference: true, throwIfNotFound: true).IsNull);
break;
}
}
}
finally
{
_nodeFactory.ManifestMetadataTable._mutableModule.ModuleThatIsCurrentlyTheSourceOfNewReferences = null;
}
}

if (!_nodeFactory.CompilationModuleGroup.VersionsWithMethodBody(method))
{
// Validate that the typedef tokens for all of the instantiation parameters of the method
Expand Down Expand Up @@ -977,5 +1027,6 @@ public string GetReproInstructions(MethodDesc method)
{
return _printReproInstructions(method);
}

}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Library</OutputType>
<AssemblyName>ILCompiler.ReadyToRun</AssemblyName>
Expand Down Expand Up @@ -45,6 +45,8 @@
<Compile Include="..\..\Common\System\FormattingHelpers.cs" Link="Common\FormattingHelpers.cs" />
<Compile Include="..\..\Common\TypeSystem\IL\ILProvider.cs" Link="IL\ILProvider.cs" />
<Compile Include="..\..\Common\TypeSystem\IL\ILReader.cs" Link="IL\ILReader.cs" />
<Compile Include="..\..\Common\TypeSystem\IL\ILStubReferences.cs" Link="IL\ILStubReferences.cs" />
<Compile Include="..\..\Common\TypeSystem\IL\Stubs\AsyncThunks.cs" Link="IL\Stubs\AsyncThunks.cs" />
<Compile Include="..\..\Common\TypeSystem\IL\Stubs\AsyncResumptionStub.cs" Link="IL\Stubs\AsyncResumptionStub.cs" />
<Compile Include="..\..\Common\TypeSystem\IL\Stubs\AsyncResumptionStub.Sorting.cs" Link="IL\Stubs\AsyncResumptionStub.Sorting.cs" />
<Compile Include="..\..\Common\TypeSystem\IL\Stubs\AsyncResumptionStub.Mangling.cs" Link="IL\Stubs\AsyncResumptionStub.Mangling.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1405,14 +1405,20 @@ private ModuleToken HandleToModuleToken(ref CORINFO_RESOLVED_TOKEN pResolvedToke
{
if (resultMethod is IL.Stubs.PInvokeTargetNativeMethod rawPinvoke)
resultMethod = rawPinvoke.Target;
if (resultMethod is AsyncMethodVariant asyncVariant)
resultMethod = asyncVariant.Target;

// It's okay to strip the instantiation away because we don't need a MethodSpec
// token - SignatureBuilder will generate the generic method signature
// using instantiation parameters from the MethodDesc entity.
resultMethod = resultMethod.GetTypicalMethodDefinition();

Debug.Assert(resultMethod is EcmaMethod);
Debug.Assert(_compilation.NodeFactory.CompilationModuleGroup.VersionsWithType(((EcmaMethod)resultMethod).OwningType));
if (!_compilation.NodeFactory.CompilationModuleGroup.VersionsWithType(((EcmaMethod)resultMethod).OwningType))
{
ModuleToken result = _compilation.NodeFactory.Resolver.GetModuleTokenForMethod(resultMethod, allowDynamicallyCreatedReference: true, throwIfNotFound: true);
return result;
}
token = (mdToken)MetadataTokens.GetToken(((EcmaMethod)resultMethod).Handle);
module = ((EcmaMethod)resultMethod).Module;
}
Expand All @@ -1432,7 +1438,11 @@ private ModuleToken HandleToModuleToken(ref CORINFO_RESOLVED_TOKEN pResolvedToke
{
if (resultDef is EcmaType ecmaType)
{
Debug.Assert(_compilation.NodeFactory.CompilationModuleGroup.VersionsWithType(ecmaType));
if (!_compilation.NodeFactory.CompilationModuleGroup.VersionsWithType(ecmaType))
{
ModuleToken result = _compilation.NodeFactory.Resolver.GetModuleTokenForType(ecmaType, allowDynamicallyCreatedReference: true, throwIfNotFound: true);
return result;
}
token = (mdToken)MetadataTokens.GetToken(ecmaType.Handle);
module = ecmaType.Module;
}
Expand Down
Loading