[wasm][R2R] crossgen2 NotImplementedException in ModuleTokenResolver with --embed-pgo-data + --opt-cross-module
- Dominant language
- C#
- Stars
- 18.3k
- Forks
- 5.6k
- PR merge metrics
- PR metrics pending
Description
## Summary
crossgen2 aborts with an unhandled `NotImplementedException` when compiling browser-wasm ReadyToRun images that combine embedded PGO instrumentation data (`--embed-pgo-data`) with cross-module optimization (`--opt-cross-module:*`).
This blocks per-app R2R publish on any build leg where `EnableNgenOptimization=true` (e.g. the `checked` CI legs), because [`eng/codeOptimization.targets`](https://github.com/dotnet/runtime/blob/main/eng/codeOptimization.targets) then sets `IncludeMibcFilesInReadyToRun=true`, feeds `StandardOptimizationData.mibc`, and appends `--embed-pgo-data` to the crossgen2 command line.
## Repro
1. Build CoreCLR + libraries for `browser-wasm` with `EnableNgenOptimization=true` (the `checked` libraries legs qualify).
2. Publish a `browser-wasm` app with `PublishReadyToRun=true` so the crossgen2 command line includes **both** `--embed-pgo-data` and `--opt-cross-module:*`.
- In CI this is the `browser-wasm linux checked CoreCLR_ReleaseLibraries` and `browser-wasm windows checked CoreCLR_ReleaseLibraries` legs.
3. crossgen2 crashes:
```
artifacts/bin/Crossgen2Tasks/Release/Microsoft.NET.CrossGen.targets(497,5): error MSB6006:
".../crossgen2" exited with code 134 (linux) / 57005 (windows)
Microsoft.NET.CrossGen.targets(377,5): error NETSDK1096: Optimizing assemblies for performance failed.
```
Underlying exception (captured from a `checked` crossgen2 run):
```
System.NotImplementedException: [System.Text.Json]System.Text.Json.JsonDocument+d__85
at ...DependencyAnalysis.ReadyToRun.ModuleTokenResolver.GetModuleTokenForType(TypeDesc, bool, bool)
in ModuleTokenResolver.cs:line 92
at ...SignatureBuilder.EmitMethodSignature(...)
at ...InstanceEntryPointTableNode.BuildSignatureForMethodDefinedInModule(MethodDesc, NodeFactory)
at ...InstrumentationDataTableNode.GetData(NodeFactory, bool) in InstrumentationDataTableNode.cs:line 274
at ...ReadyToRunObjectWriter.EmitReadyToRunObjects(...)
```
## Root cause
`InstrumentationDataTableNode.GetData` emits a method signature for every method carrying PGO data via `InstanceEntryPointTableNode.BuildSignatureForMethodDefinedInModule` ([InstrumentationDataTableNode.cs#L274](https://github.com/dotnet/runtime/blob/main/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InstrumentationDataTableNode.cs#L274)). Building that signature resolves module tokens for the types referenced in the method's signature through `ModuleTokenResolver.GetModuleTokenForType(type, allowDynamicallyCreatedReference, throwIfNotFound: true)`.
For a cross-module method whose signature references a type that has no token in any module of the cross-module compile group (here the async state machine `System.Text.Json...JsonDocument+d__85`), every reverse-lookup branch fails and the resolver throws `NotImplementedException` ([ModuleTokenResolver.cs#L90-L92](https://github.com/dotnet/runtime/blob/main/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ModuleTokenResolver.cs#L90-L92)).
Notes:
- Specific to the `--embed-pgo-data` + `--opt-cross-module` combination. The `Release` legs don't set `EnableNgenOptimization`, so they don't embed PGO and don't crash.
- Not a wasm-JIT NYI, so `JitWasm{,Simd}NyiToR2RUnsupported` does not help.
- There is no wasm-specific PGO profile — `eng/codeOptimization.targets` feeds the generic `StandardOptimizationData.mibc` for all targets, so the mibc contents are not the trigger; the emission path is.
## Fix proposal
Make the instrumentation-data signature emitter tolerate a method whose signature can't be tokenized in the current cross-module group instead of hard-crashing:
- In the `InstrumentationDataTableNode` path, resolve tokens with `throwIfNotFound: false` and **skip** (drop the PGO entry for) any method whose signature can't be fully resolved; **or**
- Ensure the referenced types are added to the manifest / version-bubble module so a token exists; **or**
- Exclude such cross-module methods from the embedded PGO set up front.
## Temporary workaround (applied on the wasm R2R branch)
Disable `--embed-pgo-data` / `IncludeMibcFilesInReadyToRun` for `browser`/`wasi` per-app R2R in `eng/codeOptimization.targets` (there is no wasm PGO profile to gain from), with a TODO linking to this issue. To be reverted once the crossgen2 fix above lands.
> [!NOTE]
> This issue description was generated with the assistance of GitHub Copilot.
Contributor guide
Research direction
Start with InstrumentationDataTableNode.cs around line 274 and trace its call to ModuleTokenResolver.cs around lines 90-92. Reproduce the browser-wasm checked publish with both --embed-pgo-data and --opt-cross-module, then inspect the existing eng/codeOptimization.targets workaround. Done means crossgen2 no longer aborts for this combination and the checked wasm R2R publish succeeds.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp, wasm
- Domain
- build-system, compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100