Enable compilation of async thunks in composite mode#126904
Enable compilation of async thunks in composite mode#126904jtschuster wants to merge 73 commits intodotnet:mainfrom
Conversation
* Add crossgen CI analysis agentic workflow * Use Opus
* Add crossgen CI analysis agentic workflow * Use Opus * Fix DIFC integrity filtering and improve issue quality in crossgen2 CI triage - Add min-integrity: none to tools.github so the agent can read all dotnet/runtime issues regardless of author association (fixes false positive 'new' issues when existing issues were invisible) - Require specific fully qualified test names and verbatim error output in created issues instead of summaries - Add pull-requests: read permission to fix compilation warning - Recompile lock.yml Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add a new test suite at src/tests/readytorun/crossmoduleresolution/ that tests R2R cross-module reference resolution across different assembly categories: Assembly graph: - A (main) - compilation target - B (version bubble, --inputbubbleref) - C (cross-module-inlineable only, --opt-cross-module) - D (external/transitive dependency) - E (type forwarder to D) Test scenarios cover: - TypeRef, MethodCall, FieldAccess across version bubble and cross-module-only - Transitive dependencies (C → D) - Nested types, type forwarders, mixed-origin generics - Interface dispatch with cross-module interfaces Two test modes via separate .csproj files: - main_crossmodule: --opt-cross-module:assemblyC (MutableModule #:N resolution) - main_bubble: --inputbubbleref:assemblyB.dll (MODULE_ZAPSIG encoding) Also adds CrossModuleResolutionTestPlan.md documenting all 5 planned phases including composite mode, ALC, and programmatic R2R validation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add async Task<T> and async Task variants of each cross-module inlineable method in assemblyC, with corresponding test methods in main.cs. Enable runtime-async compilation via Features=runtime-async=on and --opt-async-methods crossgen2 flag in both test projects. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add r2rvalidate tool using ILCompiler.Reflection.ReadyToRun to programmatically verify R2R compilation artifacts: - Assembly references (MSIL + manifest metadata AssemblyRef tables) - CHECK_IL_BODY fixups proving cross-module inlining occurred - RuntimeFunction counts confirming async thunk generation (3+) Integrate validator into test precommands for both main_crossmodule and main_bubble test variants. Validator runs after crossgen2 and before the actual test execution. For non-composite images, reads MSIL AssemblyRefs via GetGlobalMetadata().MetadataReader (not ManifestReferenceAssemblies which only holds extra manifest-level refs). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace ~160 lines of inline bash/batch in each csproj with a shared crossmoduleresolution.targets file that generates scripts from MSBuild items and properties. Each csproj now declaratively specifies: - Crossgen2Step items (dependency assemblies with extra args) - Crossgen2MainRef items (references for main assembly) - R2RExpect* items (validation expectations) - Crossgen2CommonArgs/MainExtraArgs properties The targets file generates: - A __crossgen2() helper function (bash) / :__cg2_invoke subroutine (batch) - IL_DLLS copy loop from Crossgen2Step + main assembly names - Per-step crossgen2 calls via @() item transforms - Main assembly compilation with extra args and --map - R2R validation invocation with args from R2RExpect* items Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace inline bash/batch precommands with a C# RunCrossgen tool invoked at MSBuild build time via corerun. The targets file now uses an AfterTargets=Build Exec target that runs runcrossgen.dll to: - Copy IL assemblies before crossgen2 overwrites them - Run crossgen2 on each dependency in declared order - Run crossgen2 on the main assembly with refs and flags - Run r2rvalidate for R2R image validation This moves all crossgen2 work from test execution time to build time, making the generated test scripts clean (no precommands). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Set OutputPath to $(CORE_ROOT)/runcrossgen/ so the tool lives alongside crossgen2 and corerun. The targets file now references it from CORE_ROOT instead of navigating relative to the test output directory. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Set RuntimeIdentifier=$(NETCoreSdkRuntimeIdentifier) since runcrossgen runs at build time on the build machine, not at test time on the target. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Roslyn-based test framework for crossgen2 R2R validation: - Compiles test assemblies with Roslyn at test time - Invokes crossgen2 out-of-process via dotnet exec - Validates R2R output using ILCompiler.Reflection.ReadyToRun - 4 test cases: basic inlining, transitive refs, async, composite - Integrated into crossgen2.slnx and clr.toolstests subset Status: crossgen2 invocation works, fixup validation WIP. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Use FixupKind enum + ToString(SignatureFormattingOptions) instead of ToString() which returned the class name for TodoSignature fixups - Add System.Private.CoreLib from native/ dir to crossgen2 references - Use dotnet exec instead of corerun to invoke crossgen2 - Remove opt-async-methods plumbing (unrelated to runtime-async) - Rename AsyncMethodThunks to AsyncCrossModuleInlining with correct expectations (CHECK_IL_BODY fixups, not RuntimeFunction counts) - Remove unused CoreRun property from TestPaths - Add KEEP_R2R_TESTS env var to preserve temp dirs for debugging All 4 tests pass: BasicCrossModuleInlining, TransitiveReferences, AsyncCrossModuleInlining, CompositeBasic. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add 5 new tests covering key runtime-async crossgen2 PRs: - RuntimeAsyncMethodEmission (dotnet#124203): Validates [ASYNC] variant entries for Task/ValueTask-returning methods - RuntimeAsyncContinuationLayout (dotnet#123643): Validates ContinuationLayout and ResumptionStubEntryPoint fixups for methods with GC refs across awaits - RuntimeAsyncDevirtualize (dotnet#125420): Validates async virtual method devirtualization produces [ASYNC] entries for sealed/interface dispatch - RuntimeAsyncNoYield (dotnet#124203): Validates async methods without await still produce [ASYNC] variants - RuntimeAsyncCrossModule (dotnet#121679): Validates MutableModule async references work with cross-module inlining of runtime-async methods Infrastructure changes: - Add Roslyn feature flag support (runtime-async=on) to R2RTestCaseCompiler - Add R2RExpectations for async variants, resumption stubs, continuation layouts, and arbitrary fixup kinds - Add MainExtraSourceResourceNames to R2RTestCase for shared source files - Add null guard for method.Fixups in CheckFixupKinds All 9 tests pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…dd typed Crossgen2Option - Rename DependencyInfo -> CompiledAssembly with IsCrossgenInput property - Remove AdditionalReferences: compile dependencies in listed order, each referencing all previously compiled assemblies - Add Crossgen2OptionKind enum and Crossgen2Option record replacing raw string CLI args - Remove unused CrossgenOptions from CompiledAssembly - Remove dead ReadEmbeddedSource locals in async tests - Fix Crossgen2Dir fallback to check for crossgen2.dll file instead of just directory existence (empty Debug dir was preventing Checked fallback) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…back - R2RTestCase now takes an Action<ReadyToRunReader> Validate callback instead of an R2RExpectations data object - Move CompositeMode, Crossgen2Options, Features to R2RTestCase directly - Convert R2RResultChecker to R2RAssert static helper class with methods: HasManifestRef, HasInlinedMethod, HasAsyncVariant, HasResumptionStub, HasContinuationLayout, HasResumptionStubFixup, HasFixupKind - Delete unused Expectations/R2RExpectationAttributes.cs (attribute-based design replaced by inline test code) - Each test now owns its assertions directly, making them more readable and easier to extend with custom checks Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Fix --opt-cross-module arg emission: use space-separated args without .dll suffix instead of colon-joined format - Add -composite suffix to composite output FilePath to prevent component stubs from overwriting the composite image - Add IsComposite property to CrossgenCompilation Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
InliningInfoSection2: - Add InliningEntry struct, GetEntries(), GetInliningPairs() - Method name resolution via local method map and OpenReferenceAssembly R2RResultChecker: - HasInlinedMethod checks both CrossModuleInlineInfo and all per-assembly InliningInfo2 sections (needed for composite images) - Add HasContinuationLayout(reader, methodName) overload - Add HasResumptionStubFixup(reader, methodName) overload - Add HasFixupKindOnMethod generic helper - GetAllInliningInfo2Sections iterates global + all assembly headers Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
New tests covering the intersection of cross-module inlining, runtime-async, and composite mode: - CompositeCrossModuleInlining, CompositeTransitive - CompositeAsync, CompositeAsyncCrossModuleInlining, CompositeAsyncTransitive (skipped: async not in composite yet) - AsyncCrossModuleContinuation, AsyncCrossModuleTransitive - MultiStepCompositeConsumer, CompositeAsyncDevirt, RuntimeAsyncNoYield Fix existing tests: BasicCrossModuleInlining (InlineableLib as Reference), TransitiveReferences (CrossModuleOptimization on lib). Use method-targeted HasContinuationLayout/HasResumptionStubFixup. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add C# source files for CompositeAsync, MultiStepConsumer, AsyncCrossModuleContinuation, AsyncTransitiveMain, CompositeAsyncDevirt test cases and their dependencies. Add CrossModuleInliningInfoSection parser for ILCompiler.Reflection. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…sertion In composite mode, crossgen2 skips the DebuggableAttribute-based optimization detection (Program.cs:588), defaulting to OptimizationMode.None which sets CORJIT_FLAG_DEBUG_CODE and disables all inlining. Add Crossgen2Option.Optimize to all composite compilations. Add HasInlinedMethod assertion to CompositeCrossModuleInlining test to verify cross-assembly inlining actually occurs in composite mode. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- CrossModuleInliningInfoSection: Fix cross-module inliner index parsing. The writer emits absolute ILBody indices (baseIndex is never updated), and the runtime reads them as absolute. The parser was incorrectly accumulating them as deltas, which would break for inlinees with 2+ cross-module inliners. - R2RDriver: Read stdout/stderr concurrently via ReadToEndAsync to avoid the classic redirected-stdio deadlock when crossgen2 fills one pipe buffer while we block reading the other. - R2RResultChecker: Load PE images into memory via File.ReadAllBytes instead of holding FileStream handles open. IAssemblyMetadata has no disposal contract and ReadyToRunReader caches instances indefinitely, so stream-backed implementations leak file handles. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add a test that exercises the cross-module INLINER parsing path in CrossModuleInliningInfoSection, where multiple cross-module inliner entries exist for the same inlinee. The test uses two generic types (GenericWrapperA<T>, GenericWrapperB<T>) from a --opt-cross-module reference library, each with a virtual method that inlines the same Utility.GetValue(). Value type instantiations (LocalStruct) are required because CrossModuleCompileable discovery uses CanonicalFormKind.Specific, which preserves value types (unlike reference types that canonicalize to __Canon, losing alternate location info). This produces two distinct cross-module inliner MethodDefs for the same inlinee, validating that the parser correctly reads absolute (not delta-accumulated) inliner indices. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Rename HasMultipleCrossModuleInliners to HasCrossModuleInliners and accept expected inliner method names instead of just a count. This ensures the absolute-encoded ILBody import indices actually resolve to the correct methods (GenericWrapperA, GenericWrapperB). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
These are local session artifacts that should not be in the repository. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
These tests were superseded by the ILCompiler.ReadyToRun.Tests test suite which compiles and validates R2R images programmatically. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- R2RResultChecker: fix broken see-cref (R2RTestCase.Validate -> CrossgenCompilation.Validate) - R2RTestRunner: fix comment on BuildReferencePaths (runtime pack only, not compiled assemblies) - CrossModuleInliningInfoSection: fix version to v6.2 per readytorun.h - AsyncMethods.cs: describe actual test behavior (cross-module inlining, not RuntimeFunction layout) - BasicAsyncEmission.cs: remove false claim about resumption stubs - AsyncDevirtualize.cs: describe actual validation ([ASYNC] variants, not devirtualized calls) - AsyncCrossModule.cs: describe actual validation (manifest refs + [ASYNC] variants) - AsyncCrossModuleContinuation.cs: remove false claim about ContinuationLayout fixups - R2RTestSuites: fix 'and' -> 'or' for inlining info sections, fix A+B -> A ref, clarify devirt test Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove the two gates that prevented async method variants (runtime-async) from being compiled in composite R2R mode: 1. ReadyToRunILProvider.NeedsCrossModuleInlineableTokens: Removed the '!VersionsWithModule(SystemModule)' condition for async stubs. Async thunks and resumption stubs use compiler-generated IL with synthetic tokens created by ILEmitter.NewToken() that ALWAYS need rewriting into the MutableModule via ManifestModuleWrappedMethodIL, regardless of whether CoreLib is in the version bubble. 2. CorInfoImpl.ShouldSkipCompilation: Removed the blanket composite-mode skip for IsCompilerGeneratedILBodyForAsync() methods. This was a workaround for the missing wrapping in (1) — with the root cause fixed, async stubs compile correctly in composite mode. Fix two test issues: - CompositeAsyncCrossModuleInlining: Validate sync inlining (GetValueSync) instead of async inlining (await doesn't produce traditional inlining). - MultiStepCompositeAndNonCompositeAsync: Fix assembly reference mismatch by adding a proper MultiStepAsyncConsumer source that calls AsyncCompositeLib. All 20 R2R tests pass including the 5 previously-skipped composite async tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Composite mode does produce a CrossModuleInlineInfo section when an inlineable method comes from an assembly outside the version bubble (passed as a Reference with --opt-cross-module). This complements CompositeDoesNotProduceCrossModuleInliningInfo, which covers the case where all inlinees are composite inputs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Composite mode and the emission of Async methods are fairly orthogonal and don't need tests just validation the existence of async method.
…xternalReference test
The new forceOwningTypeNotDerivedFromToken constructor parameter affects signature emission via OwningTypeNotDerivedFromToken, but equality previously only compared Field and Token. Since FieldWithToken is used as a cache key (e.g., NodeCache<FieldWithToken, Import>), instances differing only by this flag could incorrectly share cached signatures. Mirrors the existing MethodWithToken.Equals behavior. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The MethodDesc branch already forwards strippedInstantiation as forceOwningTypeFromMethodDesc; mirror that for the FieldDesc branch so that faux-IL stripped-instantiation tokens correctly mark OwningTypeNotDerivedFromToken rather than discarding the flag. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The FieldDesc branch of ComputeRuntimeLookupForSharedGenericToken is not expected to encounter stripped instantiations from faux IL. Add a Debug.Assert to catch any future regression while keeping the forwarding to FieldWithToken as a defensive fallback for retail. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
| ModuleToken _HandleToModuleToken(ref CORINFO_RESOLVED_TOKEN pResolvedToken, MethodDesc methodDesc, out TypeSystemEntity context, ref TypeDesc constrainedType, out bool strippedInstantiation) | ||
| { | ||
| if ((CorTokenType)(unchecked((uint)pResolvedToken.token) & 0xFF000000u) == CorTokenType.mdtMethodDef && | ||
| methodDesc?.GetTypicalMethodDefinition() is EcmaMethod ecmaMethod) | ||
| if (methodDesc != null && (_compilation.NodeFactory.CompilationModuleGroup.VersionsWithMethodBody(methodDesc) | ||
| || (pResolvedToken.tokenType == CorInfoTokenKind.CORINFO_TOKENKIND_DevirtualizedMethod) | ||
| || methodDesc.IsPInvoke)) | ||
| { | ||
| mdToken token = (mdToken)MetadataTokens.GetToken(ecmaMethod.Handle); | ||
| if ((CorTokenType)(unchecked((uint)pResolvedToken.token) & 0xFF000000u) == CorTokenType.mdtMethodDef && | ||
| methodDesc?.GetTypicalMethodDefinition() is EcmaMethod ecmaMethod) | ||
| { | ||
| mdToken token = (mdToken)MetadataTokens.GetToken(ecmaMethod.Handle); | ||
|
|
||
| // This is used for de-virtualization of non-generic virtual methods, and should be treated | ||
| // as a the methodDesc parameter defining the exact OwningType, not doing resolution through the token. | ||
| context = null; | ||
| constrainedType = null; | ||
| // This is used for de-virtualization of non-generic virtual methods, and should be treated | ||
| // as a the methodDesc parameter defining the exact OwningType, not doing resolution through the token. | ||
| context = null; | ||
| constrainedType = null; | ||
| strippedInstantiation = false; | ||
|
|
||
| return new ModuleToken(ecmaMethod.Module, token); | ||
| return new ModuleToken(ecmaMethod.Module, token); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| if (pResolvedToken.tokenType == CorInfoTokenKind.CORINFO_TOKENKIND_ResolvedStaticVirtualMethod) | ||
| { | ||
| context = null; | ||
| } | ||
| else | ||
| { | ||
| context = entityFromContext(pResolvedToken.tokenContext); | ||
| } | ||
| if (pResolvedToken.tokenType == CorInfoTokenKind.CORINFO_TOKENKIND_ResolvedStaticVirtualMethod) | ||
| { | ||
| context = null; | ||
| } | ||
| else | ||
| { | ||
| context = entityFromContext(pResolvedToken.tokenContext); | ||
| } | ||
|
|
||
| return HandleToModuleToken(ref pResolvedToken); | ||
| return HandleToModuleToken(ref pResolvedToken, out strippedInstantiation); | ||
| } |
There was a problem hiding this comment.
GitHub doesn't show this well, but this just moved to a local method since it's not used anywhere else.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 18 out of 18 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs:166
CreateCrossModuleInlineableTokensForILBodynow always wrapsEcmaMethodIL.Create((EcmaMethod)method.GetPrimaryMethodDesc().GetTypicalMethodDefinition()). Whenmethodis anAsyncMethodVariant(the caller comment inCorInfoImpl.reportInliningDecisionexplicitly notes this can happen), this produces a wrapped IL whoseOwningMethodis the underlyingEcmaMethod, not theAsyncMethodVariantkey you store it under. This can break token resolution and compilation becauseMethodIL.OwningMethodno longer matches the method being compiled. Consider restoring support for wrappingAsyncEcmaMethodIL/explicit owning-method (as the previous overload did) so the wrapped IL preservesOwningMethod == methodfor async variants.
public void CreateCrossModuleInlineableTokensForILBody(MethodDesc method)
{
// This method accepts only method definitions, but accepts non-primary method definitions.
// That is, it must not be generic, but it may represent an AsyncVariant
Debug.Assert(method.IsTypicalMethodDefinition);
Debug.Assert(_manifestMutableModule != null);
var wrappedMethodIL = new ManifestModuleWrappedMethodIL();
Debug.Assert(!_compilationModuleGroup.VersionsWithMethodBody(method) &&
_compilationModuleGroup.CrossModuleInlineable(method));
if (!wrappedMethodIL.Initialize(_manifestMutableModule, EcmaMethodIL.Create((EcmaMethod)method.GetPrimaryMethodDesc().GetTypicalMethodDefinition())))
{
// If we could not initialize the wrapped method IL, we should store a null.
// That will result in the IL code for the method being unavailable for use in
// the compilation, which is version safe.
wrappedMethodIL = null;
}
_manifestModuleWrappedMethods.Add(method, wrappedMethodIL);
IncrementVersion();
}
…TokensAreAvailable ExternalReferenceTokenManager.EnsureDefTokensAreAvailable now manages this flag itself when referencesAreForAsyncMethod is true, so the manual try/finally in EnsureAsyncThunkTokensAreAvailable is redundant. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 17 out of 17 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (1)
src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs:110
- FieldWithToken.AppendMangledName does not incorporate OwningTypeNotDerivedFromToken (or the owning type override). Since OwningTypeNotDerivedFromToken changes the emitted field signature (READYTORUN_FIELD_SIG_OwnerType), two distinct FieldWithToken instances could end up producing identical symbol names, risking node/symbol collisions when both variants are present. Consider appending a stable marker (and owning type, if needed) to the mangled name when OwningTypeNotDerivedFromToken is true, similar to MethodWithToken.AppendMangledName.
public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
{
sb.Append(nameMangler.GetMangledFieldName(Field));
sb.Append("; "u8);
sb.Append(Token.ToString());
}
| OwningTypeNotDerivedFromToken = true; | ||
| } | ||
| } | ||
|
|
There was a problem hiding this comment.
MethodWithToken/FieldWithToken now use OwningTypeNotDerivedFromToken to control READYTORUN_*_SIG_OwnerType emission. However, the GenericLookupKey cache uses RuntimeDeterminedTypeHelper.Equals/GetHashCode for MethodWithToken/FieldWithToken, which currently ignores OwningTypeNotDerivedFromToken. This can cause generic lookup helper imports to be reused across signatures that differ only by the owner-type-override flag, leading to incorrect fixup signatures. Update the RuntimeDeterminedTypeHelper comparisons/hashes (or GenericLookupKey equality) to include the owning-type-override flag (and any other fields that affect signature encoding).
| public bool Equals(MethodWithToken other) | |
| { | |
| return EqualityComparer<MethodDesc>.Default.Equals(Method, other.Method) | |
| && Token.Equals(other.Token) | |
| && EqualityComparer<TypeDesc>.Default.Equals(ConstrainedType, other.ConstrainedType) | |
| && Unboxing == other.Unboxing | |
| && OwningTypeNotDerivedFromToken == other.OwningTypeNotDerivedFromToken | |
| && EqualityComparer<TypeDesc>.Default.Equals(OwningType, other.OwningType); | |
| } | |
| public override bool Equals(object obj) | |
| { | |
| return obj is MethodWithToken other && Equals(other); | |
| } | |
| public override int GetHashCode() | |
| { | |
| HashCode hash = new HashCode(); | |
| hash.Add(Method); | |
| hash.Add(Token); | |
| hash.Add(ConstrainedType); | |
| hash.Add(Unboxing); | |
| hash.Add(OwningTypeNotDerivedFromToken); | |
| hash.Add(OwningType); | |
| return hash.ToHashCode(); | |
| } | |
| public static bool operator ==(MethodWithToken left, MethodWithToken right) | |
| { | |
| return left.Equals(right); | |
| } | |
| public static bool operator !=(MethodWithToken left, MethodWithToken right) | |
| { | |
| return !left.Equals(right); | |
| } |
davidwrighton
left a comment
There was a problem hiding this comment.
I like it! I'm assuming you've run the various crossgen2 outerloops, as I can't actually validate these sorts of changes by hand in a reliable manner. I have one question around changing the behavior of the FieldWithToken and MethodToken comparisons, but otherwise, it looks reasonable.
| else | ||
| return -1; | ||
| } | ||
| result = OwningTypeNotDerivedFromToken.CompareTo(other.OwningTypeNotDerivedFromToken); |
There was a problem hiding this comment.
These details around the OwningTypeNotDerivedFromToken are interesting. I expect that adding these comparisons to the logic will increase the size of R2R images by not sharing some cases which could be sharing. Could you do an experiment where instead of adding a compare based on OwningTypeNotDerivedFromToken, you keep the forceOwningTypeFromMethodDesc flag on the MethodToken, and add that to the comparison instead? You'd also need to tweak these asserts, but it might be a size win.
| break; | ||
| } | ||
| } | ||
| if (forceOwningTypeNotDerivedFromToken) |
There was a problem hiding this comment.
I have the same concerns around adding the OwningTypeNotDerivedFromToken to the comparisons here as I do for MethodToken.
|
@jtschuster Also, I was perusing code, and noticed that |
|
/azp run runtime-coreclr crossgen2 |
|
Azure Pipelines successfully started running 1 pipeline(s). |
The issue with async thunks in composite mode was that we emit MutableModule tokens for the thunks that point to some types in the composite image. But using MutableModule tokens pointing to anything but CoreLib is explicitly blocked - without an IL module to determine the ALC, assembly load behavior might not be preserved. To work around this, I went back to the approach where we add missing tokens to the mutable module and compile the thunk as an ILStub. Since the thunks should only need to reference existing type refs/defs accessible from the composite image + some async helpers in CoreLib, this shouldn't end up adding any tokens to the manifest module that point anywhere but CoreLib.
The process of going through an IL method body and creating tokens in the MutableModule is handled by the ExternalReferenceTokenManager (open to renames).
This implementation initially caused some issues when emitting signatures for Generic methods on generic types. In resolving a module token for a method on an IL stub (CorInfoImpl.HandleToModuleToken()), we strip the instantiation info and get a token for the method definition. The comment in that method assumed this is fine because when emitting the signature, we add flags and generic instantiation info for the method. But when MethodWithToken..ctor() calculates the owning type, it assumes the ModuleToken is for the generic instantiated method on a generic instantiated type (but we just stripped off the instantiation). To account for that, the code now will tell the MethodWithToken to force the OwningType to be MethodDesc.OwningType and force it to emit the OwningType flag it in the Signature.
fixes #125337