Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
2a0dfd7
Remove async method inlining restrictions in crossgen2
jtschuster Mar 10, 2026
1cd97cf
Merge branch 'main' into InlineAsync
jtschuster Mar 12, 2026
e6796ba
Merge branch 'main' into InlineAsync
jtschuster Mar 16, 2026
124470c
Intern AsyncResumptionStub instances to fix sort crash with --opt-cro…
jtschuster Mar 25, 2026
b697bd6
Address PR feedback: remove casts, delete R2R field, improve hash
jtschuster Mar 26, 2026
ebed8f8
Remove _asyncResumptionStub field from RyuJit CorInfoImpl
jtschuster Mar 26, 2026
f4462aa
Fix extra blank line and guard AddResumptionStubFixup against duplica…
jtschuster Mar 26, 2026
437e5a7
Remove _resumptionStubFixupAdded guard, rely on existing dedup
jtschuster Mar 26, 2026
cdb0f80
Merge branch 'fix-async-resumption-stub-interning' into InlineAsync
jtschuster Mar 26, 2026
0cbae01
Enable cross-module async method inlining in crossgen2
jtschuster Mar 26, 2026
de28114
Fix CreateCrossModuleInlineableTokensForILBody ordering for async var…
jtschuster Mar 26, 2026
284c58d
Move async inline test to readytorun tree with crossgen2 precommands
jtschuster Mar 26, 2026
d8f0fc8
Fix XML doc tag casing and typo in ILBodyFixupSignature
jtschuster Mar 26, 2026
f508c70
Remove accidentally committed r2rdump file
jtschuster Mar 26, 2026
f15732e
Merge branch 'main' of https://github.com/dotnet/runtime into InlineA…
jtschuster Mar 27, 2026
3d2c7d6
Fix MayHaveILHeader assert for async IL body fixups
jtschuster Mar 27, 2026
86e95cc
Use signatureMethod for EmitMethodSignature in ILBodyFixupSignature
jtschuster Mar 27, 2026
febd446
Add comments explaining IL/signature method split for async fixups
jtschuster Mar 27, 2026
b6982ba
Simplify: single GetModuleToken, add GetPrimaryMethodDesc assert
jtschuster Mar 27, 2026
7b11660
Undo changes to runtime
jtschuster Mar 27, 2026
00578a2
Add SignatureMethod to ILBodyFixupSignature to control what method si…
jtschuster Mar 27, 2026
1ea7bc4
Allow CheckILBody fixup to encode non-Ecma method signatures
jtschuster Mar 30, 2026
e983977
Merge branch 'main' of https://github.com/dotnet/runtime into InlineA…
jtschuster Mar 31, 2026
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
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,42 @@
using Internal.CorConstants;
using Internal.JitInterface;

using ILCompiler.ReadyToRun.TypeSystem;

namespace ILCompiler.DependencyAnalysis.ReadyToRun
{
/// <summary>
/// This fixup instructs the runtime to validate that the IL found at runtime matches the hash of the IL computed at compile time.
/// </summary>
/// <remarks>
/// The fixup encodes two distinct pieces of information:
/// <list type="bullet">
/// <item>The IL body hash, computed from the <see cref="ILMethod"/> (the underlying EcmaMethod whose metadata
/// contains the IL). This is derived from _signatureMethod via GetPrimaryMethodDesc().</item>
/// <item>The method identity (_signatureMethod), encoded in the fixup signature. The runtime decodes this
/// back to a MethodDesc via ZapSig::DecodeMethod and reads the IL at that method's RVA to compare against
/// the hash.</item>
/// </list>
/// For most methods, _signatureMethod is already the EcmaMethod. For runtime-async methods, the JIT inlines
/// an AsyncMethodVariant. The EcmaMethod for AsyncMethodVariant can be retrieved with GetPrimaryMethodDesc().
/// </remarks>
public class ILBodyFixupSignature : Signature, IEquatable<ILBodyFixupSignature>
{
private readonly ReadyToRunFixupKind _fixupKind;
private readonly EcmaMethod _method;

public ILBodyFixupSignature(ReadyToRunFixupKind fixupKind, EcmaMethod ecmaMethod)
/// <summary>The method identity encoded in the fixup signature that the runtime decodes to locate the IL at its RVA.</summary>
private readonly MethodDesc _signatureMethod;

/// <summary>The underlying EcmaMethod whose IL body from metadata is hashed for the fixup validation.</summary>
private EcmaMethod ILMethod => (EcmaMethod)_signatureMethod.GetPrimaryMethodDesc();

public ILBodyFixupSignature(ReadyToRunFixupKind fixupKind, MethodDesc signatureMethod)
{
Debug.Assert(signatureMethod.IsTypicalMethodDefinition);
Debug.Assert(!signatureMethod.IsCompilerGeneratedILBodyForAsync());
Comment thread
jtschuster marked this conversation as resolved.
Debug.Assert(signatureMethod.GetPrimaryMethodDesc() is EcmaMethod);
_fixupKind = fixupKind;
_method = ecmaMethod;
_signatureMethod = signatureMethod;
}

public override int ClassCode => 308579267;
Expand All @@ -44,10 +69,11 @@ public static void NotifyComplete(NodeFactory factory, List<ILBodyFixupSignature

private ModuleToken GetModuleToken(NodeFactory factory)
{
if (factory.CompilationModuleGroup.VersionsWithMethodBody(_method))
return new ModuleToken(_method.Module, _method.Handle);
EcmaMethod ilMethod = ILMethod;
if (factory.CompilationModuleGroup.VersionsWithMethodBody(ilMethod))
return new ModuleToken(ilMethod.Module, ilMethod.Handle);
else
return new ModuleToken(factory.ManifestMetadataTable._mutableModule, factory.ManifestMetadataTable._mutableModule.TryGetEntityHandle(_method.GetTypicalMethodDefinition()).Value);
return new ModuleToken(factory.ManifestMetadataTable._mutableModule, factory.ManifestMetadataTable._mutableModule.TryGetEntityHandle(ilMethod.GetTypicalMethodDefinition()).Value);
}

public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
Expand All @@ -63,7 +89,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
IEcmaModule targetModule = moduleToken.Module;
SignatureContext innerContext = dataBuilder.EmitFixup(factory, _fixupKind, targetModule, factory.SignatureContext);

var metadata = ReadyToRunStandaloneMethodMetadata.Compute(_method);
var metadata = ReadyToRunStandaloneMethodMetadata.Compute(ILMethod);
dataBuilder.EmitUInt(checked((uint)metadata.ConstantData.Length));
dataBuilder.EmitBytes(metadata.ConstantData);
dataBuilder.EmitUInt(checked((uint)metadata.TypeRefs.Length));
Expand All @@ -77,7 +103,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
dataBuilder.EmitTypeSignature(typeRef, innerContext);
}

MethodWithToken method = new MethodWithToken(_method, moduleToken, null, unboxing: false, context: null);
MethodWithToken method = new MethodWithToken(_signatureMethod, moduleToken, null, unboxing: false, context: null);
dataBuilder.EmitMethodSignature(method, enforceDefEncoding: false, enforceOwningType: false, innerContext, false);
}

Expand All @@ -88,7 +114,7 @@ public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilde
{
sb.Append(nameMangler.CompilationUnitPrefix);
sb.Append($@"ILBodyFixupSignature({_fixupKind.ToString()}): ");
sb.Append(nameMangler.GetMangledMethodName(_method));
sb.Append(nameMangler.GetMangledMethodName(ILMethod));
}

public override int CompareToImpl(ISortableNode other, CompilerComparer comparer)
Expand All @@ -98,12 +124,12 @@ public override int CompareToImpl(ISortableNode other, CompilerComparer comparer
if (result != 0)
return result;

return comparer.Compare(_method, otherNode._method);
return comparer.Compare(_signatureMethod, otherNode._signatureMethod);
}

public override string ToString()
{
return $"ILBodyFixupSignature {_fixupKind} {_method}";
return $"ILBodyFixupSignature {_fixupKind} {_signatureMethod}";
}

public bool Equals(ILBodyFixupSignature other) => object.ReferenceEquals(other, this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
if (relocsOnly)
return new ObjectData(Array.Empty<byte>(), Array.Empty<Relocation>(), 1, new ISymbolDefinitionNode[] { this });

Dictionary<EcmaMethod, HashSet<EcmaMethod>> inlineeToInliners = new Dictionary<EcmaMethod, HashSet<EcmaMethod>>();
Dictionary<MethodDesc, HashSet<MethodDesc>> inlineeToInliners = new Dictionary<MethodDesc, HashSet<MethodDesc>>();

// Build a map from inlinee to the list of inliners
// We are only interested in the generic definitions of these.
Expand All @@ -72,11 +72,6 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
continue;
}
MethodDesc inliner = methodNode.Method;
if (inliner.IsCompilerGeneratedILBodyForAsync())
{
// Async thunks are restricted from inlining. https://github.com/dotnet/runtime/issues/124665
continue;
}
EcmaMethod inlinerDefinition = (EcmaMethod)inliner.GetPrimaryMethodDesc().GetTypicalMethodDefinition();

if (inlinerDefinition.IsNonVersionable())
Expand All @@ -93,7 +88,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
foreach (MethodDesc inlinee in inlinees)
{
MethodDesc inlineeDefinition = inlinee.GetTypicalMethodDefinition();
if (!(inlineeDefinition is EcmaMethod ecmaInlineeDefinition))
if (!(inlineeDefinition is EcmaMethod or AsyncMethodVariant))
{
// We don't record non-ECMA methods because they don't have tokens that
// diagnostic tools could reason about anyway.
Expand All @@ -114,7 +109,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
{
if (!inlinerReportAllVersionsWithInlinee)
{
// We'll won't report this method
// We won't report this method
continue;
}
}
Expand All @@ -123,17 +118,17 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
Debug.Assert(factory.CompilationModuleGroup.CrossModuleInlineable(inlineeDefinition));
if (_inlineInfoType != InfoType.CrossModuleInliningForCrossModuleDataOnly)
{
// We'll won't report this method
// We won't report this method
continue;
}
}

if (!inlineeToInliners.TryGetValue(ecmaInlineeDefinition, out HashSet<EcmaMethod> inliners))
if (!inlineeToInliners.TryGetValue(inlineeDefinition, out HashSet<MethodDesc> inliners))
{
inliners = new HashSet<EcmaMethod>();
inlineeToInliners.Add(ecmaInlineeDefinition, inliners);
inliners = new HashSet<MethodDesc>();
inlineeToInliners.Add(inlineeDefinition, inliners);
}
inliners.Add((EcmaMethod)inlinerDefinition);
inliners.Add(inlinerDefinition);
}
}

Expand All @@ -146,8 +141,9 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)

foreach (var inlineeWithInliners in inlineeToInliners)
{
EcmaMethod inlinee = inlineeWithInliners.Key;
int inlineeRid = MetadataTokens.GetRowNumber(inlinee.Handle);
MethodDesc inlinee = inlineeWithInliners.Key;
EcmaMethod ecmaInlinee = (EcmaMethod)inlinee.GetPrimaryMethodDesc();
int inlineeRid = MetadataTokens.GetRowNumber(ecmaInlinee.Handle);
int hashCode;

if (AllowCrossModuleInlines)
Expand All @@ -158,7 +154,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
else
{
// InliningInfo2 format
hashCode = VersionResilientHashCode.ModuleNameHashCode(inlinee.Module);
hashCode = VersionResilientHashCode.ModuleNameHashCode(ecmaInlinee.Module);
hashCode ^= inlineeRid;
}

Expand All @@ -173,14 +169,19 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
// Followed by inliner RIDs deltas with flag in the lowest bit
// - if flag is set, followed by module ID
Debug.Assert(_module != null);
bool isForeignInlinee = inlinee.Module != _module;
bool isForeignInlinee = ecmaInlinee.Module != _module;
sig.Append(new UnsignedConstant((uint)(inlineeRid << 1 | (isForeignInlinee ? 1 : 0))));
if (isForeignInlinee)
{
sig.Append(new UnsignedConstant((uint)factory.ManifestMetadataTable.ModuleToIndex(inlinee.Module)));
sig.Append(new UnsignedConstant((uint)factory.ManifestMetadataTable.ModuleToIndex(ecmaInlinee.Module)));
}

List<EcmaMethod> sortedInliners = new List<EcmaMethod>(inlineeWithInliners.Value);
// We're only concerned with metadata here, so we can convert all to EcmaMethod and lose info about AsyncVariant vs Task-Returning
List<EcmaMethod> sortedInliners = new List<EcmaMethod>(inlineeWithInliners.Value.Count);
foreach (var inliner in inlineeWithInliners.Value)
{
sortedInliners.Add((EcmaMethod)inliner.GetPrimaryMethodDesc());
}
sortedInliners.MergeSort((a, b) =>
{
if (a == b)
Expand Down Expand Up @@ -302,7 +303,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
}
else
{
indexOfInlinee = (uint)MetadataTokens.GetRowNumber(inlinee.Handle);
indexOfInlinee = (uint)MetadataTokens.GetRowNumber(ecmaInlinee.Handle);
}

encodedInlinee = indexOfInlinee << (int)ReadyToRunCrossModuleInlineFlags.CrossModuleInlinerIndexShift;
Expand All @@ -315,7 +316,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)

sig.Append(new UnsignedConstant(encodedInlinee));
if (crossModuleMultiModuleFormat && !isCrossModuleInlinee)
sig.Append(new UnsignedConstant((uint)factory.ManifestMetadataTable.ModuleToIndex(inlinee.Module)));
sig.Append(new UnsignedConstant((uint)factory.ManifestMetadataTable.ModuleToIndex(ecmaInlinee.Module)));

int inlinerIndex = 0;
if (crossModuleInlinerCount > 0)
Expand All @@ -339,7 +340,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
uint inlinerRid = (uint)MetadataTokens.GetRowNumber(inliner.Handle);
uint ridDelta = inlinerRid - baseRid;
baseRid = inlinerRid;
bool isForeignInliner = inliner.Module != inlinee.Module;
bool isForeignInliner = inliner.Module != ecmaInlinee.Module;
Debug.Assert(!isForeignInliner || crossModuleMultiModuleFormat);

if (crossModuleMultiModuleFormat)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -644,11 +644,12 @@ public VirtualResolutionFixupSignature VirtualResolutionFixupSignature(ReadyToRu
private struct ILBodyFixupSignatureFixupKey : IEquatable<ILBodyFixupSignatureFixupKey>
{
public readonly ReadyToRunFixupKind FixupKind;
public readonly EcmaMethod Method;
public readonly MethodDesc Method;

public ILBodyFixupSignatureFixupKey(ReadyToRunFixupKind fixupKind, EcmaMethod method)
public ILBodyFixupSignatureFixupKey(ReadyToRunFixupKind fixupKind, MethodDesc method)
{
FixupKind = fixupKind;
Debug.Assert(method.IsTypicalMethodDefinition);
Method = method;
}
public bool Equals(ILBodyFixupSignatureFixupKey other) => FixupKind == other.FixupKind && Method.Equals(other.Method);
Expand All @@ -660,7 +661,7 @@ public ILBodyFixupSignatureFixupKey(ReadyToRunFixupKind fixupKind, EcmaMethod me
private NodeCache<ILBodyFixupSignatureFixupKey, ILBodyFixupSignature> _ilBodySignatures =
new NodeCache<ILBodyFixupSignatureFixupKey, ILBodyFixupSignature>((key) => new ILBodyFixupSignature(key.FixupKind, key.Method));

public ILBodyFixupSignature ILBodyFixupSignature(ReadyToRunFixupKind fixupKind, EcmaMethod method)
public ILBodyFixupSignature ILBodyFixupSignature(ReadyToRunFixupKind fixupKind, MethodDesc method)
{
return _ilBodySignatures.GetOrAdd(new ILBodyFixupSignatureFixupKey(fixupKind, method));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using Internal.TypeSystem.Ecma;
using Internal.ReadyToRunConstants;
using Internal.Text;
using System.Diagnostics;

namespace ILCompiler.DependencyAnalysis
{
Expand Down Expand Up @@ -507,8 +508,9 @@ public Import CheckVirtualFunctionOverride(MethodWithToken declMethod, TypeDesc
}

private NodeCache<ILBodyFixupSignature, Import> _ilBodyFixupsCache;
public Import CheckILBodyFixupSignature(EcmaMethod method)
public Import CheckILBodyFixupSignature(MethodDesc method)
{
Debug.Assert(method.IsTypicalMethodDefinition);
return _ilBodyFixupsCache.GetOrAdd(_codegenNodeFactory.ILBodyFixupSignature(
_verifyTypeAndFieldLayout ? ReadyToRunFixupKind.Verify_IL_Body : ReadyToRunFixupKind.Check_IL_Body,
method));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,12 +109,6 @@ public bool CanInline(MethodDesc caller, MethodDesc callee)
}
}

// TODO: Enable async inlining. https://github.com/dotnet/runtime/issues/124665
if (callee.IsAsyncThunk() || callee.IsAsyncCall())
{
return false;
}

_nodeFactory.DetectGenericCycles(caller, callee);

return NodeFactory.CompilationModuleGroup.CanInline(caller, callee);
Expand Down Expand Up @@ -655,14 +649,19 @@ public bool IsInheritanceChainLayoutFixedInCurrentVersionBubble(TypeDesc type)
// The _finishedFirstCompilationRunInPhase2 variable works in concert some checking to ensure that we don't violate any of this model
private bool _finishedFirstCompilationRunInPhase2 = false;

public void PrepareForCompilationRetry(MethodWithGCInfo methodToBeRecompiled, IEnumerable<EcmaMethod> methodsThatNeedILBodies)
public void PrepareForCompilationRetry(MethodWithGCInfo methodToBeRecompiled, IEnumerable<MethodDesc> methodsThatNeedILBodies)
{
lock (_methodsToRecompile)
{
_methodsToRecompile.Add(methodToBeRecompiled);
if (methodsThatNeedILBodies != null)
{
foreach (var method in methodsThatNeedILBodies)
{
Debug.Assert(method.IsMethodDefinition);
_methodsWhichNeedMutableILBodies.Add(method);
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -520,10 +520,6 @@ private bool CrossModuleInlineableInternal(MethodDesc method)

private bool CrossModuleInlineableUncached(MethodDesc method)
{
// Async thunks and variants cannot currently be inlined cross module
if (method.IsAsyncVariant() || method.IsAsync || method.IsAsyncThunk())
return false;

// Defined in corelib
MetadataType owningMetadataType = method.OwningType.GetTypeDefinition() as MetadataType;
if (owningMetadataType == null)
Expand Down
Loading
Loading