Skip to content

Compare trimmable typemap APK contents#11346

Draft
simonrozsival wants to merge 6 commits into
dev/simonrozsival/remove-forceunconditionalentriesfrom
dev/simonrozsival/compare-trimmable-typemap-trimming
Draft

Compare trimmable typemap APK contents#11346
simonrozsival wants to merge 6 commits into
dev/simonrozsival/remove-forceunconditionalentriesfrom
dev/simonrozsival/compare-trimmable-typemap-trimming

Conversation

@simonrozsival
Copy link
Copy Markdown
Member

@simonrozsival simonrozsival commented May 14, 2026

Stacked on #11345.

This PR adds an end-to-end Release CoreCLR HelloWorld APK comparison between _AndroidTypeMapImplementation=llvm-ir and trimmable, using TrimMode=full and AndroidLinkTool=r8. The test reads assembly-store contents, dex contents, generated Java sources, and typemap metadata, then asserts the trimmable path does not retain extra typemap-eligible managed or Java entries.

It also fixes the trimmable CoreCLR linker/R8 integration:

  • Mark generated typemap DLLs as trimmable.
  • Pass generated per-assembly *.TypeMap.dll assemblies to ILLink with --typemap-entry-assembly, because the actual TypeMapAttribute entries live there.
  • Generate proguard keep rules from linked assemblies for the CoreCLR trimmable typemap path, matching the llvm-ir path.
  • Treat SDK framework ACWs as conditional unless explicitly rooted, so Mono.Android implementors are not kept unconditionally just because they are generated ACWs.

Latest R8-backed comparison

Metric llvm-ir trimmable
APK size 15,653,142 15,268,118
Assembly-store payload 6,406,325 6,010,476
classes*.dex 396,172 234,464
Registered managed types / methods 65 / 732 44 / 373
Managed diff 380 llvm-ir-only 0 trimmable-only
Java diff 25 llvm-ir-only 0 trimmable-only

Per-dex contents

Build Dex file Size Classes
llvm-ir classes.dex 396,172 347
trimmable classes.dex 234,464 340

Detailed counts

Metric llvm-ir trimmable
Managed assemblies 16 15
Raw managed types / methods 2,058 / 15,645 1,961 / 14,627
Raw Java classes / methods 347 / 2,544 340 / 2,587
Filtered Java classes / methods 4 / 21 0 / 0
Dex string ids 3,042 2,276
Dex type ids 888 869
Dex proto ids 327 303
Dex field ids 665 344
Dex method ids 2,623 2,664
Dex data size 339,008 183,192
Generated Java sources 5 316
__md_methods files 1 0
Runtime.register files 1 0
Runtime.registerNatives files 0 313

Minimal ProGuard experiment

A follow-up experiment reduced trimmable classes.dex to 13,008 bytes / 20 classes, but device validation showed the APK did not run: launching MainActivity crashed with UnsatisfiedLinkError for MainActivity.nctor_0(). That experiment was reverted in adc8960d6, so this PR currently keeps the runtime-safe R8 configuration shown above.

Typemap metadata checks

Generated typemap assemblies now show no duplicate TypeMapAttribute keys. The _Mono.Android.TypeMap.dll unconditional count dropped from 332 to 13 after making framework ACWs conditional by default.

Assembly TypeMap attributes Unique Unconditional Conditional Associations
_TypemapComparison.TypeMap.dll 1 1 1 0 1
_Microsoft.Android.TypeMaps.dll 0 0 0 0 0
_Mono.Android.TypeMap.dll 7,328 7,328 13 7,315 9,071
_Java.Interop.TypeMap.dll 3 3 0 3 3

_Microsoft.Android.TypeMaps.dll also has 3 TypeMapAssemblyTargetAttribute entries. The earlier 44 number is not a typemap-entry count; it is the final packaged registered managed type count after filtering helper/runtime plumbing.

Validation

  • dotnet test tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests.csproj --no-restore --nologo
  • `MSBUILDDISABLENODEREUSE=1 dotnet build src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj --no-restore --no-dependencies --nologo -p:Configuration=Debug -nr:false -m:1 -p:BuildInParallel=false
  • MSBUILDDISABLENODEREUSE=1 dotnet build src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Xamarin.Android.Build.Tests.csproj --no-restore --no-dependencies --nologo -p:Configuration=Debug -nr:false -m:1 -p:BuildInParallel=false
  • MSBUILDDISABLENODEREUSE=1 ./dotnet-local.sh test bin/TestDebug/net10.0/Xamarin.Android.Build.Tests.dll --filter Name~ReleaseCoreClrTrimmableTypeMap_DoesNotKeepMoreTypemapPeersThanLlvmIr
  • ./dotnet-local.sh test bin/TestDebug/net11.0/Microsoft.Android.Sdk.TrimmableTypeMap.IntegrationTests.dll --nologo
  • git diff --check

Add a Release CoreCLR HelloWorld comparison test that builds llvm-ir and trimmable typemap APKs, prints managed and dex diagnostics, and asserts the trimmable typemap does not retain extra typemap-eligible managed or Java entries.

Pass all generated typemap assemblies to ILLink as typemap entry assemblies and mark them trimmable so conditional typemap entries are honored.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings May 14, 2026 11:54
Generate proguard keep rules for the CoreCLR trimmable typemap path from the linked assemblies, matching the llvm-ir path, and make the APK comparison test use R8 so dex contents are actually shrunk.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival simonrozsival marked this pull request as draft May 14, 2026 12:01
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 strengthens trimmable typemap validation and updates CoreCLR ILLink integration so generated typemap assemblies participate in trimming and typemap entry discovery.

Changes:

  • Adds a Release CoreCLR APK comparison test between llvm-ir and trimmable typemap modes.
  • Parses assembly-store and DEX contents to compare managed/Java typemap-retained entries.
  • Updates trimmable CoreCLR targets to mark generated typemap DLLs trimmable and pass them to ILLink.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/TrimmableTypeMapBuildTests.cs Adds APK profiling/comparison helpers and a new Release CoreCLR typemap retention test.
src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.CoreCLR.targets Updates ILLink inputs/arguments for generated trimmable typemap assemblies.

Comment on lines 25 to +28
<Target Name="_ConfigureTrimmableTypeMapForLinker"
BeforeTargets="PrepareForILLink;_RunILLink"
DependsOnTargets="_GenerateTrimmableTypeMap">
<ItemGroup>
<Target Name="_ConfigureTrimmableTypeMapForLinker"
BeforeTargets="PrepareForILLink;_RunILLink"
DependsOnTargets="_GenerateTrimmableTypeMap">
<ItemGroup>

var javaName = fields [1].Trim ();
if (!IsTypemapHelperJavaType (javaName)) {
profile.CandidateJavaNames.Add (javaName);
simonrozsival and others added 4 commits May 14, 2026 14:08
Limit --typemap-entry-assembly arguments to generated per-assembly *.TypeMap.dll assemblies that contain TypeMapAttribute entries, instead of treating the root typemap loader assembly as an entry assembly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Keep SDK framework ACWs conditional unless they are explicitly rooted, and pass framework assembly names through trimmable typemap generation. This allows Mono.Android implementor entries to be trimmed while preserving app ACWs and scanner-rooted components.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Generate R8 keep rules for trimmable typemap builds from linked assemblies instead of the pre-link acw-map, and use a minimal Xamarin runtime keep list for this path.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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.

2 participants