Skip to content

Fix Crossgen2 MIBC parser recovery after unresolved methods - #134208

Open
simonrozsival wants to merge 3 commits into
mainfrom
simonrozsival-mibc-parser-recovery
Open

simonrozsival wants to merge 3 commits into
mainfrom
simonrozsival-mibc-parser-recovery

Conversation

@simonrozsival

Copy link
Copy Markdown
Member

Summary

Reset per-record MIBC parser state whenever a record ends, including when its method cannot be resolved. Continue skipping the unresolved record, but preserve the following valid record.

Motivation

I hit this while investigating .NET MAUI app launch performance with the default framework profiles.

MIBC profiles can reference methods that no longer exist in the current assemblies, either because APIs changed or because trimming removed them. Skipping those methods is expected; silently dropping the next valid method is not.

For example, the default MAUI profiles contained an unresolved Java.Interop.JniRuntime.CreateInvoker(IntPtr) record followed by a resolvable SetValueManager(CreationOptions) record. The parser discarded both. With partial ReadyToRun compilation, this can leave startup methods uncompiled even though they are present in the profiles.

Root cause and fix

ReadMIbcGroup marks an unresolved method with metadataNotResolvable. At the terminating pop, it resets the parser state and optional profile data only when the method is not that sentinel.

Consequently, the next record starts in LookingForOptionalData instead of LookingForNextMethod, and its method token is not captured.

This moves the existing state resets outside that condition. Adding profile data remains conditional, and valid-record processing is unchanged.

Regression test and test-design question

The test uses the existing R2RTestRunner to compile a small C# fixture and invoke Crossgen2 with --partial. It generates a minimal MIBC containing:

Missing -> AfterMissing -> Control

Missing is absent from the input assembly. R2RAssert.HasCompiledMethod checks that the generated ReadyToRun image contains native code for both AfterMissing and Control, while NotInProfile remains uncompiled. The theory covers single-assembly and composite compilation, plus valid-profile controls that omit the missing record.

I'm not sure what the best way to test this is in this area. I reused the existing compiler harness and compiled-method assertions, but didn't find a reusable test MIBC builder or an existing profile with the exact missing/valid record sequence. Generating a small profile in the test keeps that sequence explicit and independent of changing framework optimization profiles. I'd appreciate guidance on whether a checked-in profile, an IL fixture, or a shared emitter would be preferable.

Failure without the fix

I temporarily restored the unmodified parser on the current main-branch base, rebuilt, and ran the final test unchanged. Both missing-record cases failed with:

Expected compiled method 'ProfiledMethods.AfterMissing' not found.
All compiled methods (1):
  ProfiledMethods:Control int ProfiledMethods.Control()

The two valid-profile controls passed. With the fix restored, all four cases pass. BasicCrossModuleInlining and CompositeBasic also pass.

Command used for the focused regression:

./dotnet.sh test src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj \
  -c Release -p:TargetArchitecture=arm64 \
  --filter 'FullyQualifiedName~PartialCompilationAfterUnresolvedMibcMethod'

.NET MAUI startup measurements

Measured a Release/CoreCLR MAUI sample on a Samsung Galaxy A16 using the default profiles, without adding a custom recorded profile.

The performance experiment used matched compilers built from the RC2 runtime source commit 379a2093bfbda341396acfa9a829b717d0c110c5, differing only in the parser assembly and its PDB. Both compilations used identical arguments, 89 byte-identical linked assemblies, and seven byte-identical profiles: six stock MAUI profiles plus the automatically generated app-method profile. This was not a comparison between a shipped compiler and an unrelated main-branch build; the PR itself targets main.

Metric Unpatched Patched
Median startup 1,916.5 ms 1,893.5 ms
p90 startup 1,994 ms 1,921 ms
ReadyToRun methods 15,517 15,853
Native code bytes 3,376,896 3,440,516
APK bytes 16,894,848 16,931,712

Startup used Android am start -W TotalTime, with 20 measured cold-process launches per compiler in balanced A/B/B/A batches. OS caches were warm, application data was seeded, and initial launches and warmups were excluded. Orientation was fixed, and thermal status remained unthrottled throughout.

The observed median improvement was 23 ms (1.2%), recovering 336 compiled methods at an APK cost of 36 KiB. This is a modest, workload-specific result rather than a general performance guarantee.

Note

This PR was prepared with GitHub Copilot assistance.

simonrozsival and others added 2 commits September 18, 2026 19:12
Keep profile publication conditional while resetting per-record state at every pop. Cover missing methods and unavailable base assemblies, consecutive missing records, and optional-data isolation in the existing ReadyToRun parser tests.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Replace parser-only assertions with the existing ReadyToRun integration harness. Generate a precise missing/valid-method profile and use HasCompiledMethod to verify partial compilation in single-assembly and composite modes, with valid-profile controls and an unprofiled method.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/crossgen-contrib
See info in area-owners.md if you want to be subscribed.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟢 Approval recommended

The focused fix is covered by regression tests and has no unresolved blocking issues.

Review effort: Lite
Findings: None

What changed in this PR

Fixes Crossgen2 MIBC parser recovery so valid methods following unresolved records remain eligible for partial ReadyToRun compilation.

Changes:

  • Resets parser state after every MIBC record.
  • Adds fixture methods and regression coverage for single and composite compilation.
File Summary
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​IBC/​MIbcProfileParser.cs Resets per-record state after unresolved methods.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun.Tests/​TestCases/​Mibc/​Methods.cs Provides regression-test fixture methods.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun.Tests/​MIbcProfileParserTests.cs Adds MIBC recovery tests and profile generation.

Include Roslyn-compiled test assemblies in the ReadyToRunReader resolver so composite WebCIL validation can enumerate their compiled methods.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings September 19, 2026 05:56

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟢 Approval recommended

The reviewed changes include focused parser recovery fixes and regression coverage, with no unresolved blocking issues.

Review effort: Lite
Findings: None

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants