[TrimmableTypeMap] Trim value-type dictionary instantiations - #12793
Open
simonrozsival wants to merge 1 commit into
Open
simonrozsival wants to merge 1 commit into
simonrozsival wants to merge 1 commit into
Conversation
Replace the NativeAOT generic-virtual cross-product with a conditional type-map converter universe for primitive and nullable value-type dictionaries. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
simonrozsival
added this pull request to stack #12799
September 15, 2026 17:38
This was referenced Sep 15, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
NativeAOT cannot create arbitrary closed generic types at runtime. Reference-type generic arguments can share a canonical
__Canontemplate, but value-type arguments remain exact:JavaDictionary<int,long>,JavaDictionary<int,double>, andJavaDictionary<int?,long?>each require their own generated native code and runtime metadata.The previous implementation supported Java-to-managed conversion for every primitive and nullable value/value dictionary shape through generic virtual dispatch:
Because every one of the 18 supported primitive/nullable
ValueTypeFactory<T>instantiations was reachable, NativeAOT's generic-virtual dependency analysis conservatively generated the complete 18 × 18 matrix. Consequently, every trimmable NativeAOT application paid for all 324JavaDictionary<TKey,TValue>instantiations even when the application used no value/value dictionary—or only one combination. Size analysis showedJavaDictionary<TKey,TValue>contributing roughly 1.4 MB of the managed NativeAOT image.The desired behavior is for each exact generic instantiation to follow normal application reachability: using
IDictionary<int,long>should retain its converter andJavaDictionary<int,long>constructor, without retaining unrelated combinations.Approach
This change replaces the generic-virtual cross-product with a handwritten conditional type-map universe:
TypeMap<JavaDictionary>entries associate each supported target shape with a small converter type and use the target shape as the trim target.IDictionary<TKey,TValue>and concreteJavaDictionary<TKey,TValue>targets are represented.new JavaDictionary<TKey,TValue>(...)call.ValueTypeDictionaryFactoryperforms a string-keyed external type-map lookup. String keys are important because probing an unlisted closed generic through the proxy map requires itsTypeHandle; NativeAOT may intentionally have no EEType for that unsupported shape.Mono.Androidfor this universe. The map entries remain conditional, so unused converters and closed generic instantiations can still be removed.JavaDictionaryfallback.The universe explicitly describes all 18 primitive and nullable value types in each key/value position. Unlike the previous GVM implementation, listing all supported combinations does not mean all combinations enter every application: the trimmer includes an entry only when its trim-target type is used.
APK size
An identical
dotnet new mauiapp was published as an unsigned Releaseandroid-arm64APK with the trimmable typemap. The baseline is this stack's exactmainparent (8f7c4d4fa5).The NativeAOT app library itself decreased from 24,045,952 B to 21,440,264 B: -2,605,688 B (-10.84%) uncompressed. CoreCLR does not pay for NativeAOT generic code generation; its small increase is the retained typemap metadata/runtime machinery.
Validation