Skip to content

[net11.0] R2R-compile only System.Private.CoreLib for Debug CoreCLR mobile builds#25583

Open
kotlarmilos wants to merge 5 commits into
net11.0from
dev/kotlarmilos/r2r-only-corelib-in-debug
Open

[net11.0] R2R-compile only System.Private.CoreLib for Debug CoreCLR mobile builds#25583
kotlarmilos wants to merge 5 commits into
net11.0from
dev/kotlarmilos/r2r-only-corelib-in-debug

Conversation

@kotlarmilos

@kotlarmilos kotlarmilos commented May 29, 2026

Copy link
Copy Markdown
Member

Description

In Debug builds with CoreCLR on Apple mobile, narrow _NonUserAssemblies to System.Private.CoreLib so that crossgen2 only ReadyToRun-compiles CoreLib and every other framework is excluded from R2R via the existing PublishReadyToRunExclude path. Non-R2R'd methods fall back to the interpreter at runtime. The .app bundle shrinks from 395 MB to 132 MB, with a corresponding reduction in crossgen2 time during build. Startup is unchanged in practice because the hot path through corelib is still R2R.

…obile builds

In Debug builds with CoreCLR on iOS, narrow _NonUserAssemblies to System.Private.CoreLib so that crossgen2 only ReadyToRun-compiles CoreLib and every other framework, MAUI, and NuGet assembly is excluded from R2R via the existing PublishReadyToRunExclude path. Non-R2R'd methods fall back to the CoreCLR interpreter at runtime (JIT is forbidden on iOS), which is the desired inner-loop behavior. Measured on SampleMAUI publish for ios-arm64 Debug: framework dylib drops from 318 MB to 28 MB and the .app bundle shrinks from 395 MB to 132 MB, with a corresponding reduction in crossgen2 time during build. Startup is unchanged in practice because the hot path through CoreLib is still R2R'd; only first-call cost for non-CoreLib methods shifts onto the interpreter, which is negligible for the inner-loop scenarios this default targets. Release builds are unaffected (FilterReadyToRunAssemblies already gates _SelectR2RAssemblies off for Release).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings May 29, 2026 14:07

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.

Pull request overview

This PR changes Debug CoreCLR ReadyToRun filtering for macho builds so only System.Private.CoreLib is R2R-compiled, reducing Debug app size/build time while leaving other assemblies interpreted.

Changes:

  • Narrows _NonUserAssemblies to System.Private.CoreLib.
  • Routes all other assemblies through the existing PublishReadyToRunExclude path.
  • Keeps Release behavior unchanged through the existing FilterReadyToRunAssemblies gating.
Show a summary per file
File Description
dotnet/targets/Microsoft.Sdk.R2R.targets Updates the Debug CoreCLR R2R assembly selection filter to retain only CoreLib.

Copilot's findings

  • Files reviewed: 1/1 changed files
  • Comments generated: 2

Comment thread dotnet/targets/Microsoft.Sdk.R2R.targets Outdated
Comment thread dotnet/targets/Microsoft.Sdk.R2R.targets Outdated
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@rolfbjarne

Copy link
Copy Markdown
Member

/azp run

@rolfbjarne rolfbjarne self-assigned this Jun 1, 2026
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 3 pipeline(s).

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@rolfbjarne rolfbjarne enabled auto-merge (squash) June 2, 2026 06:02
@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@kotlarmilos

Copy link
Copy Markdown
Member Author

/cc: @vitek-karas @davidnguyen-tech

@rolfbjarne rolfbjarne disabled auto-merge June 2, 2026 10:06
@rolfbjarne rolfbjarne enabled auto-merge (squash) June 3, 2026 07:27
@rolfbjarne

Copy link
Copy Markdown
Member

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 3 pipeline(s).

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@rolfbjarne

Copy link
Copy Markdown
Member

@kotlarmilos the test failures (crashes) look related to this PR:

Thread 0 Crashed::  Dispatch queue: com.apple.main-thread
0   libsystem_kernel.dylib        	       0x104bcc85c __pthread_kill + 8
1   libsystem_pthread.dylib       	       0x104fae2a8 pthread_kill + 264
2   libsystem_c.dylib             	       0x18017b984 abort + 116
3   libcoreclr.dylib              	       0x1045f4610 PROCAbort + 68
4   libcoreclr.dylib              	       0x1045f44e0 RaiseFailFastException + 28
5   libcoreclr.dylib              	       0x1043dd360 NativeImage::Open(SString const&, char const*, AssemblyBinder*, LoaderAllocator*, bool) + 1832
6   libcoreclr.dylib              	       0x104345918 AssemblyBinder::LoadNativeImage(Module*, char const*, bool) + 152
7   libcoreclr.dylib              	       0x104422194 ReadyToRunInfo::Initialize(Module*, AllocMemTracker*) + 528
8   libcoreclr.dylib              	       0x10434a908 Module::Initialize(AllocMemTracker*, char16_t const*) + 412
9   libcoreclr.dylib              	       0x104349890 Module::DoInit(AllocMemTracker*, char16_t const*) + 308
10  libcoreclr.dylib              	       0x10434b020 Module::Create(Assembly*, PEAssembly*, AllocMemTracker*) + 336
11  libcoreclr.dylib              	       0x10433fcf8 Assembly::Init(AllocMemTracker*) + 276
12  libcoreclr.dylib              	       0x10434098c Assembly::Create(PEAssembly*, AllocMemTracker*, LoaderAllocator*) + 524
13  libcoreclr.dylib              	       0x1043398dc AppDomain::TryIncrementalLoad(FileLoadLevel, LifetimeHolder<FileLoadLock::HolderTraits>&) + 192
14  libcoreclr.dylib              	       0x104338f98 AppDomain::LoadAssembly(FileLoadLock*, FileLoadLevel) + 336
15  libcoreclr.dylib              	       0x104339274 AppDomain::LoadAssemblyInternal(AssemblySpec*, PEAssembly*, FileLoadLevel) + 384
16  libcoreclr.dylib              	       0x104335f0c AppDomain::LoadAssembly(AssemblySpec*, PEAssembly*, FileLoadLevel) + 188
17  libcoreclr.dylib              	       0x10434e794 Module::LoadAssemblyImpl(unsigned int) + 300
18  libcoreclr.dylib              	       0x104341f20 Assembly::FindModuleByTypeRef(ModuleBase*, unsigned int, Loader::LoadFlag, int*) + 652
19  libcoreclr.dylib              	       0x10435de60 ClassLoader::LoadTypeDefOrRefThrowing(ModuleBase*, unsigned int, ClassLoader::NotFoundAction, ClassLoader::PermitUninstantiatedFlag, unsigned int, ClassLoadLevel) + 500
20  libcoreclr.dylib              	       0x10435e9a0 ClassLoader::LoadApproxParentThrowing(Module*, unsigned int, SigPointer*, SigTypeContext const*) + 132
21  libcoreclr.dylib              	       0x1044abdf0 ClassLoader::CreateTypeHandleForTypeDefThrowing(Module*, unsigned int, Instantiation, AllocMemTracker*) + 496
22  libcoreclr.dylib              	       0x10435ee98 ClassLoader::CreateTypeHandleForTypeKey(TypeKey const*, AllocMemTracker*) + 772
23  libcoreclr.dylib              	       0x10436002c ClassLoader::LoadTypeHandleForTypeKey_Body(TypeKey const*, TypeHandle, ClassLoadLevel) + 792
24  libcoreclr.dylib              	       0x10435c784 ClassLoader::LoadTypeHandleForTypeKey(TypeKey const*, TypeHandle, ClassLoadLevel, InstantiationContext const*) + 216
25  libcoreclr.dylib              	       0x10435d270 ClassLoader::LoadTypeDefThrowing(Module*, unsigned int, ClassLoader::NotFoundAction, ClassLoader::PermitUninstantiatedFlag, unsigned int, ClassLoadLevel, Instantiation*) + 368
26  libcoreclr.dylib              	       0x1043c4368 MemberLoader::GetMethodDescFromMethodDef(Module*, unsigned int, int, ClassLoadLevel) + 156
27  libcoreclr.dylib              	       0x1043a0ee0 CEEInfo::resolveToken(CORINFO_RESOLVED_TOKEN*) + 696
28  libcoreclr.dylib              	       0x104314364 InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN*, bool, bool, bool, bool) + 2096
29  libcoreclr.dylib              	       0x1043083c4 InterpCompiler::GenerateCode(CORINFO_METHOD_INFO*) + 19392
30  libcoreclr.dylib              	       0x104302ce4 InterpCompiler::CompileMethod() + 88
31  libcoreclr.dylib              	       0x10431ec4c CILInterp::compileMethod(ICorJitInfo*, CORINFO_METHOD_INFO*, unsigned int, unsigned char**, unsigned int*) + 324
32  libcoreclr.dylib              	       0x1043b86a8 UnsafeJitFunction(PrepareCodeConfig*, COR_ILMETHOD_DECODER*, bool*, bool*, unsigned int*) + 900
33  libcoreclr.dylib              	       0x1043e7fc4 MethodDesc::JitCompileCodeLocked(PrepareCodeConfig*, COR_ILMETHOD_DECODER*, ListLockEntryBase<NativeCodeVersion>*, unsigned int*, bool*) + 160
34  libcoreclr.dylib              	       0x1043e7a34 MethodDesc::JitCompileCodeLockedEventWrapper(PrepareCodeConfig*, ListLockEntryBase<NativeCodeVersion>*) + 748
35  libcoreclr.dylib              	       0x1043e73c0 MethodDesc::JitCompileCode(PrepareCodeConfig*) + 436
36  libcoreclr.dylib              	       0x1043e711c MethodDesc::PrepareCode(PrepareCodeConfig*) + 388
37  libcoreclr.dylib              	       0x1043e9ac4 MethodDesc::DoPrestub(MethodTable*, CallerGCMode) + 896
38  libcoreclr.dylib              	       0x1043e9220 PreStubWorker + 644
39  libcoreclr.dylib              	       0x1045b8448 ThePreStub + 80
40  monotouchtest.r2r             	       0x1054b5ec0 S_P_CoreLib_System_Runtime_CompilerServices_InitHelpers__CallClassConstructor + 64
41  libcoreclr.dylib              	       0x1043d1d88 void UnmanagedCallersOnlyCaller::InvokeThrowing<unsigned long, MethodTable*>(unsigned long, MethodTable*) + 172
42  libcoreclr.dylib              	       0x1043d1940 MethodTable::RunClassInitEx(Object**) + 324
43  libcoreclr.dylib              	       0x1043d200c MethodTable::DoRunClassInitThrowing() + 424
44  libcoreclr.dylib              	       0x104344de8 Assembly::DoIncrementalLoad(FileLoadLevel) + 356
45  libcoreclr.dylib              	       0x104339934 AppDomain::TryIncrementalLoad(FileLoadLevel, LifetimeHolder<FileLoadLock::HolderTraits>&) + 280
46  libcoreclr.dylib              	       0x104338f98 AppDomain::LoadAssembly(FileLoadLock*, FileLoadLevel) + 336
47  libcoreclr.dylib              	       0x104339274 AppDomain::LoadAssemblyInternal(AssemblySpec*, PEAssembly*, FileLoadLevel) + 384
48  libcoreclr.dylib              	       0x104335f0c AppDomain::LoadAssembly(AssemblySpec*, PEAssembly*, FileLoadLevel) + 188
49  libcoreclr.dylib              	       0x104434f5c AssemblySpec::LoadAssembly(FileLoadLevel, int) + 248
50  libcoreclr.dylib              	       0x104364af4 CorHost2::CreateDelegate(unsigned int, char16_t const*, char16_t const*, char16_t const*, long*) + 560
51  libcoreclr.dylib              	       0x1045d5700 coreclr_create_delegate + 112

…eLib-only R2R composite

Restricting the R2R composite to only System.Private.CoreLib makes the SDK
flag every other framework assembly as hidden from the composite and drop it
from the crossgen2 -r list. The composite is then built with zero references,
producing a native image CoreCLR rejects at startup (FailFast in
NativeImage::Open). Add a target that re-injects the excluded assemblies into
_ReadyToRunCompositeBuildReferences (excluding the composite inputs), so
crossgen2 receives them via -r while they still run as plain IL. Gated to
filtered composite builds, so non-filtered builds are unaffected.

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

Copy link
Copy Markdown
Member

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 3 pipeline(s).

@kotlarmilos

kotlarmilos commented Jun 3, 2026

Copy link
Copy Markdown
Member Author

@rolfbjarne If the last commit fixes the CI, then we should proceed with dotnet/sdk#54576 instead of the workaround added here

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@rolfbjarne rolfbjarne disabled auto-merge June 3, 2026 14:46
@davidnguyen-tech

davidnguyen-tech commented Jun 3, 2026

Copy link
Copy Markdown
Member

Root cause of the crash

The crash (NativeImage::Open → RaiseFailFastException → abort) comes from how this PR excludes assemblies.

The CoreCLR runtime pack ships its BCL/framework assemblies already R2R-compiled as components of one composite native image. Each component DLL's R2R header records its owning composite by name (Microsoft.NETCore.App.Runtime.ios-arm64.r2r.dylib).

macios never ships that upstream composite. It runs crossgen2 to build a per-app composite (<App>.r2r.dylib) and registers only that name in a generated module table (GenerateR2RModuleRegistration). At load, CoreCLR resolves a component's owning composite through xamarin_get_native_code_data, which strcmps the requested owner_composite_name against that table.

  • Normal path: crossgen2 consumes the component DLLs and ships re-headered copies whose owner_composite_name = monotouchtest.r2r.dylib — which is in the table. ✅
  • This PR: the non-CoreLib assemblies go into PublishReadyToRunExclude, so crossgen2 never touches them. They ship unchanged, still pointing at owner_composite_name = Microsoft.NETCore.App.Runtime.ios-arm64.r2r.dylib — a name macios never produces or registers. The strcmp misses → FailFast. ❌

CoreLib survives because it's the one assembly still rooted into the per-app composite (frame 40, monotouchtest.r2r … CallClassConstructor); every other framework assembly hits the stale header.

Fix: instead of excluding the other assemblies, keep them as unrooted composite inputs via PublishReadyToRunCompositeRoots = System.Private.CoreLib. crossgen2 re-headers them to monotouchtest.r2r.dylib (skipping codegen) instead of leaving the stale name. This is what #25567 does.

Generated by Copilot.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@rolfbjarne

Copy link
Copy Markdown
Member

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 3 pipeline(s).

@vs-mobiletools-engineering-service2

Copy link
Copy Markdown
Collaborator

✅ [PR Build #60c8581] Build passed (Detect API changes) ✅

Pipeline on Agent
Hash: 60c858121e30f7a7f317b520aa88f42c3dce6beb [PR build]

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

Copy link
Copy Markdown
Collaborator

✅ [PR Build #60c8581] Build passed (Build packages) ✅

Pipeline on Agent
Hash: 60c858121e30f7a7f317b520aa88f42c3dce6beb [PR build]

@vs-mobiletools-engineering-service2

Copy link
Copy Markdown
Collaborator

✅ API diff for current PR / commit

NET (empty diffs)

✅ API diff vs stable

NET (empty diffs)

ℹ️ Generator diff

Generator Diff: vsdrops (html) vsdrops (raw diff) gist (raw diff) - Please review changes)

Pipeline on Agent
Hash: 60c858121e30f7a7f317b520aa88f42c3dce6beb [PR build]

@vs-mobiletools-engineering-service2

Copy link
Copy Markdown
Collaborator

✅ [PR Build #60c8581] Build passed (Build macOS tests) ✅

Pipeline on Agent
Hash: 60c858121e30f7a7f317b520aa88f42c3dce6beb [PR build]

@vs-mobiletools-engineering-service2

Copy link
Copy Markdown
Collaborator

🔥 [CI Build #60c8581] Test results 🔥

Test results

❌ Tests failed on VSTS: test results

0 tests crashed, 2 tests failed, 193 tests passed.

Failures

❌ monotouch tests (iOS)

1 tests failed, 23 tests passed.

Failed tests

  • monotouch-test/iOS - simulator/Release (CoreCLR, x64): Failed

Html Report (VSDrops) Download

❌ monotouch tests (tvOS)

1 tests failed, 23 tests passed.

Failed tests

  • monotouch-test/tvOS - simulator/Debug (ARM64): BuildFailure

Html Report (VSDrops) Download

Successes

✅ cecil: All 1 tests passed. Html Report (VSDrops) Download
✅ dotnettests (iOS): All 1 tests passed. Html Report (VSDrops) Download
✅ dotnettests (MacCatalyst): All 1 tests passed. Html Report (VSDrops) Download
✅ dotnettests (macOS): All 1 tests passed. Html Report (VSDrops) Download
✅ dotnettests (Multiple platforms): All 1 tests passed. Html Report (VSDrops) Download
✅ dotnettests (tvOS): All 1 tests passed. Html Report (VSDrops) Download
✅ framework: All 2 tests passed. Html Report (VSDrops) Download
✅ fsharp: All 4 tests passed. Html Report (VSDrops) Download
✅ generator: All 5 tests passed. Html Report (VSDrops) Download
✅ interdependent-binding-projects: All 4 tests passed. Html Report (VSDrops) Download
✅ introspection: All 9 tests passed. Html Report (VSDrops) Download
✅ linker (iOS): All 11 tests passed. Html Report (VSDrops) Download
✅ linker (MacCatalyst): All 11 tests passed. Html Report (VSDrops) Download
✅ linker (macOS): All 11 tests passed. Html Report (VSDrops) Download
✅ linker (tvOS): All 11 tests passed. Html Report (VSDrops) Download
✅ monotouch (MacCatalyst): All 27 tests passed. Html Report (VSDrops) Download
✅ monotouch (macOS): All 20 tests passed. Html Report (VSDrops) Download
✅ msbuild: All 2 tests passed. Html Report (VSDrops) Download
✅ sharpie: All 1 tests passed. Html Report (VSDrops) Download
✅ windows: All 3 tests passed. Html Report (VSDrops) Download
✅ xcframework: All 4 tests passed. Html Report (VSDrops) Download
✅ xtro: All 1 tests passed. Html Report (VSDrops) Download

macOS tests

✅ Tests on macOS Sonoma (14): All 5 tests passed. Html Report (VSDrops) Download
✅ Tests on macOS Sequoia (15): All 5 tests passed. Html Report (VSDrops) Download
✅ Tests on macOS Tahoe (26): All 5 tests passed. Html Report (VSDrops) Download

Linux Build Verification

Linux build succeeded

Pipeline on Agent
Hash: 60c858121e30f7a7f317b520aa88f42c3dce6beb [PR build]

@davidnguyen-tech

Copy link
Copy Markdown
Member

The composite-roots approach fixed the R2R crash — introspection, the linker jobs, and macOS tests are all green now. The two remaining failures on build 14281697 are unrelated to this change:

1. monotouch_ios — iOS Release (CoreCLR, x64): Security.SecProtocolMetadataTest.TlsDefaults

ServerName
  Expected: "www.microsoft.com"
  But was:  null

Flaky network test — it opens a real TLS connection and reads back the SNI server name, which comes back null when the agent's network hiccups. This is the Release configuration, which this PR doesn't touch (the R2R filter is Debug-only).

2. monotouch_tvos — tvOS Debug (ARM64): BuildFailure
The failing build's log ends abruptly mid-NuGet-restore and its binlog is truncated (Unable to read beyond the end of the stream) — i.e. the build process was killed (agent OOM/timeout), with no compiler or crossgen2 error anywhere. The other 23 tvOS app builds in the same job succeeded.

Both look like infra/flaky noise; a re-run of the two jobs should green the PR.

Generated by Copilot.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants