[TrimmableTypeMap][NativeAOT] Initialize trimmable typemap runtime#11292
Conversation
There was a problem hiding this comment.
Pull request overview
This PR wires the trimmable typemap path into NativeAOT startup and build-time ILC inputs so NativeAOT apps can initialize the managed/runtime typemap state before Java interop begins.
Changes:
- Adds NativeAOT-specific build plumbing for trimmable typemap assemblies and runtime feature switches.
- Updates NativeAOT host/runtime initialization so typemap data is initialized earlier and native registration happens after
JniRuntime.Currentis available. - Aligns NativeAOT runtime state with the Mono/CoreCLR init path for GREF/GC-user-peer setup and runtime identification.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.NativeAOT.targets |
Adds typemap assemblies to NativeAOT ILC inputs and sets the typemap entry assembly. |
src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk.NativeAOT.targets |
Adds a NativeAOT runtime feature switch to host configuration. |
src/native/nativeaot/host/host.cc |
Populates NativeAOT init args with GREF threshold and GC-user-peer class refs. |
src/Mono.Android/Microsoft.Android.Runtime/RuntimeFeature.cs |
Introduces IsNativeAotRuntime runtime identity switch. |
src/Mono.Android/Android.Runtime/JNIEnvInit.cs |
Splits NativeAOT typemap initialization/registration into explicit helper steps and applies more init state from args. |
src/Mono.Android/Android.Runtime/JNIEnv.cs |
Treats NativeAOT like CoreCLR for uncaught exception propagation. |
src/Mono.Android/Android.Runtime/AndroidRuntimeInternal.cs |
Treats NativeAOT like CoreCLR for unhandled-exception callback selection. |
src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/JavaInteropRuntime.cs |
Reorders NativeAOT startup to initialize typemap data before runtime creation and register natives afterward. |
| <ItemGroup> | ||
| <IlcReference Include="@(_GeneratedTypeMapAssemblies)" /> | ||
| <UnmanagedEntryPointsAssembly Include="@(_GeneratedTypeMapAssemblies)" /> | ||
| <_TrimmableTypeMapIlcAssemblies Include="$(_TypeMapOutputDirectory)*.dll" /> |
| <Target Name="_AddTrimmableTypeMapAssembliesToIlc" | ||
| AfterTargets="_GenerateJavaStubs" | ||
| Condition=" '@(_GeneratedTypeMapAssemblies->Count())' != '0' "> | ||
| BeforeTargets="_AndroidComputeIlcCompileInputs" | ||
| DependsOnTargets="_GenerateTrimmableTypeMap"> | ||
| <PropertyGroup> | ||
| <TypeMapEntryAssembly>$(_TypeMapAssemblyName)</TypeMapEntryAssembly> |
|
Additional NativeAOT testing update:
Arm64-only local Debug-pack measurements:
No fatal/crash logcat hits were found for these runs. The NativeAOT trimmable APK contains only |
|
Follow-up on blanket rooting for NativeAOT trimmable typemap:
Validated NativeAOT trimmable arm64 after the change: Result: I also tried disabling
So |
Wire the trimmable typemap into NativeAOT startup and ILC inputs so generated typemap assemblies are included in the NativeAOT closure and runtime state is initialized before managed peer creation. Also add an explicit NativeAOT runtime feature switch for runtime code paths that should not be treated as MonoVM. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Make ForceUnconditionalEntries configurable and disable it for NativeAOT trimmable typemap testing so framework bindings can be conditionally rooted. Keep the existing workaround enabled by default for other trimmable typemap configurations. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Keep generated framework typemap assemblies as ILC references for type-map metadata, but do not pass them as UnmanagedEntryPointsAssembly inputs. This avoids treating framework typemap assemblies as unmanaged-entrypoint roots while preserving app typemap exports. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Clarify the ForceUnconditionalEntries plumbing and make the NativeAOT unmanaged-entrypoint assembly filtering easier to read without changing behavior. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Check each NativeAOT GC peer FindClass result before continuing so a failed lookup does not leave a pending JNI exception while another JNI lookup runs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
NativeAOT trimmable typemap generation scans framework assemblies so runtime-needed framework peers remain available, but framework ACW implementors should not be blanket unconditional roots. Classify framework inputs before model generation and emit framework ACWs as conditional trim-target entries. Add summary logging and regression coverage for duplicate MSBuild input items where only one copy carries framework metadata. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Include ArrayMapAnchors.cs in Mono.Android so generated per-rank array TypeMap entries can resolve their shared Microsoft.Android.Runtime.__ArrayMapRankN anchor types at NativeAOT compile time. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
88ecd21 to
aa91d95
Compare
Summary
Fix NativeAOT startup with the trimmable typemap implementation enabled, and make the generated trimmable typemap usable for NativeAOT without rooting the full Mono.Android framework ACW closure.
This PR is currently stacked on the trimmable array typemap work in #11238 so the NativeAOT path can use the current array-entry implementation and so the PR shows the real size impact of the combined work.
Key changes:
JniRuntime.Currentis available.--typemap-entry-assemblyfor the root typemap assembly.JnienvInitializeArgswith GC-user-peer class refs and GREF threshold so managed runtime state matches the Mono/CoreCLR init path.RuntimeFeature.IsNativeAotRuntimeswitch for NativeAOT-specific runtime identity.__ArrayMapRankNanchors intoMono.Androidso NativeAOT can resolve the generated array typemap groups.Fixes #11052.
Current state
The repo HelloWorld sample now builds, installs, and launches with NativeAOT + trimmable typemap enabled.
Current clean
android-arm64Release output:3,601,583bytes1,543,717byteslibHelloWorld.DotNet.so3,481,288bytesThis is about 1.4 MiB smaller than the earlier NativeAOT managed/legacy typemap result measured in this investigation, while startup remains in the same range. The latest smoke launch on
emulator-5554was successful:The size is not the final expected floor. There is still obvious room to improve as we continue identifying and reducing NativeAOT codegen and metadata roots; this PR gets the NativeAOT trimmable typemap path into a working, measurable state so those follow-up reductions can be targeted with real data.
Validation
Results:
Xamarin.Android.Build.Tasks.csprojbuild passed.Microsoft.Android.Sdk.TrimmableTypeMap.Tests: 457/457 passed.GenerateTrimmableTypeMapTests: 9/9 passed.__ArrayMapRankILC failure.emulator-5554; logcat showedlibHelloWorld.DotNet.soloaded and no fatal exception/native crash.