dotnet / dotnet/runtime

[wasm][R2R] crossgen2 NotImplementedException in ModuleTokenResolver with --embed-pgo-data + --opt-cross-module

Open
#132,825 2 comments 0 reactions 0 assignees View on GitHub
arch-wasm area-ReadyToRun
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.