Skip to content

[tools] Add a new 'trimmable-static' registrar, that uses trimmable type maps. Fixes #23108.#25079

Open
rolfbjarne wants to merge 22 commits intomainfrom
dev/rolf/trimmable-type-map
Open

[tools] Add a new 'trimmable-static' registrar, that uses trimmable type maps. Fixes #23108.#25079
rolfbjarne wants to merge 22 commits intomainfrom
dev/rolf/trimmable-type-map

Conversation

@rolfbjarne
Copy link
Copy Markdown
Member

@rolfbjarne rolfbjarne commented Apr 3, 2026

Adopt Interop (Trimmable) Type maps

This pull request adds support for Interop (trimmable) type maps, by adding a new registrar, trimmable-static, which is really just a variation/evolution of the managed-static registrar.

The advantage is that (as the name says) the generated managed code is trimmable, which means it can be generated outside of the trimmer and doesn't need a custom linker step (for simplicity and to ease the implementation it's currently implemented as a custom linker step, but one that runs before any trimming is done, which makes it easy to move to a pre-trimming MSBuild task later on, together with all the other pre-trimming tasks that must be done).

The main difference between the trimmable-static and managed-static registrars, is that with trimmable-static we don't generate any lookup code after the trimmer has executed (the ManagedRegistrarLookupTables step), we generate the trimmable type maps instead (in a new TrimmableRegistrar step).

In a future change, we'll move the trimmable static registrar code outside of the linker, so it doesn't run as a custom linker step.

There's a significant amount of (disabled) debug logging; this will be removed once the type map support is more tested and stable.

References:

Fixes #23108.

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 introduces a new trimmable-static registrar mode that leverages .NET “Interop Type Maps” to support trimmable registrations, wiring the new mode through the linker, runtime, MSBuild targets, and test variations.

Changes:

  • Add a new linker step (TrimmableRegistrarStep) that emits type-map assemblies/attributes used for type mapping and proxy lookup.
  • Extend runtime + native runtime flags and lookup paths to support trimmable-static, including proxy-attribute based construction and unmanaged entrypoint lookup fallback.
  • Add new build/test plumbing (MSBuild properties, runtimeconfig host options, xharness variations) to exercise the new registrar mode.

Reviewed changes

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

Show a summary per file
File Description
tools/dotnet-linker/Steps/TrimmableRegistrarStep.cs New linker step that generates typemap assemblies and proxy attribute types/mappings.
tools/dotnet-linker/Steps/RegistrarStep.cs Treat TrimmableStatic similarly to managed-static in registrar output flow.
tools/dotnet-linker/Steps/ManagedRegistrarStep.cs Enable managed-registrar step for trimmable-static to generate UCO trampolines.
tools/dotnet-linker/LinkerConfiguration.cs Add EntryAssembly helper and new linker config values for typemap output/name.
tools/dotnet-linker/CecilExtensions.cs Add TryFindSingle helper for Cecil collections.
tools/dotnet-linker/AppBundleRewriter.cs Add typemap-related Cecil lookups and System.Console assembly support.
tools/dotnet-linker/.vscode/launch.json Adds a VS Code launcher configuration for debugging the linker.
tools/common/Target.cs Emit a runtime flag indicating trimmable-static registrar usage.
tools/common/StaticRegistrar.cs Adjust static registrar generation for trimmable-static and extend dlsym signature usage.
tools/common/Optimizations.cs Allow static-registrar-related optimizations for trimmable-static.
tools/common/Assembly.cs Treat trimmable-static like other static registrar modes for -force_load decisions.
tools/common/Application.cs Add RegistrarMode value + parsing + typemap config values.
tests/xharness/Jenkins/TestVariationsFactory.cs Add CI variations for trimmable-static registrar.
tests/monotouch-test/Foundation/StringTest.cs Adds extra logging in a test case (currently noisy).
tests/monotouch-test/dotnet/shared.csproj Notes/placeholder for enabling trimmable-static registrar in tests.
tests/monotouch-test/dotnet/macOS/monotouch-test.csproj Adds host configuration option for TypeMappingEntryAssembly.
tests/monotouch-test/dotnet/macOS/Makefile Adds convenience targets for trimmable-static registrar runs.
tests/monotouch-test/dotnet/macOS/.vscode/tasks.json Adds VS Code build task for monotouch-test macOS.
tests/monotouch-test/dotnet/macOS/.vscode/launch.json Adds VS Code launch profiles for running monotouch-test macOS.
tests/dotnet/MySimpleApp/shared.csproj Sets registrar to trimmable-static for MySimpleApp.
tests/common/test-variations.csproj Defines new test variations for trimmable-static registrar.
tests/common/shared-dotnet.mk Minor build log output improvement.
src/ObjCRuntime/TypeMaps.cs New runtime helper to lazily initialize typemap dictionaries.
src/ObjCRuntime/Runtime.cs Add trimmable-static runtime flag + proxy-based construction + new unmanaged lookup signature.
src/ObjCRuntime/RegistrarHelper.cs Extend unmanaged lookup to optionally resolve via typemap/proxy attribute.
src/ObjCRuntime/Registrar.cs Add IsStubClass convenience property on ObjCType.
src/ObjCRuntime/Class.cs Add trimmable typemap lookup path and introduce proxy attribute base types/universe markers.
src/ILLink.Substitutions.tvOS.xml Add substitutions for IsTrimmableStaticRegistrar.
src/ILLink.Substitutions.macOS.xml Add substitutions for IsTrimmableStaticRegistrar.
src/ILLink.Substitutions.MacCatalyst.xml Add substitutions for IsTrimmableStaticRegistrar.
src/ILLink.Substitutions.iOS.xml Add substitutions for IsTrimmableStaticRegistrar.
src/frameworks.sources Include new ObjCRuntime/TypeMaps.cs in build.
scripts/rsp-to-csproj/rsp-to-csproj.cs Add Link="..." metadata for generated csproj items when applicable.
runtime/xamarin/runtime.h Add trimmable-static registrar flag setter + extend registrar dlsym signature.
runtime/runtime.m Implement trimmable-static registrar flag + pass objc class name to unmanaged lookup.
runtime/delegates.t4 Extend managed delegate signature for unmanaged lookup to include objc class name.
dotnet/targets/Xamarin.Shared.Sdk.targets Wire registrar mode to linker steps, typemap output publishing, and host config options.

Comment thread tools/dotnet-linker/.vscode/launch.json Outdated
Comment thread tests/monotouch-test/dotnet/macOS/Makefile Outdated
Comment thread tests/monotouch-test/Foundation/StringTest.cs Outdated
Comment thread tools/common/Application.cs Outdated
Comment thread tools/common/StaticRegistrar.cs Outdated
Comment thread tests/monotouch-test/dotnet/macOS/monotouch-test.csproj Outdated
Comment thread dotnet/targets/Xamarin.Shared.Sdk.targets
Comment thread tools/dotnet-linker/Steps/TrimmableRegistrarStep.cs
Comment thread tests/dotnet/MySimpleApp/shared.csproj Outdated
Comment thread tools/dotnet-linker/Steps/TrimmableRegistrarStep.cs Outdated
@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.

@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 force-pushed the dev/rolf/trimmable-type-map branch from 33e1906 to 12d0980 Compare April 6, 2026 14:38
@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.

@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 force-pushed the dev/rolf/trimmable-type-map branch from 07bfcf7 to e758700 Compare April 7, 2026 07:11
@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.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@rolfbjarne
Copy link
Copy Markdown
Member Author

rolfbjarne commented Apr 28, 2026

🤖 Re: this comment about splitting the compound &&:

I verified this by building tests/dotnet/MySimpleApp/MacCatalyst with all three registrar modes (static, managed-static, trimmable-static) and inspecting the trimmed IL with ikdasm.

The trimmer handles the compound && correctly in all cases — the ConstructNSObject<NSObject> call (the dead code inside the if-block) is properly eliminated when the condition evaluates to false.

The only downside is a few bytes of vestigial IL (push constant + pop, or unconditional branch) left behind as artifacts of the substitution, but functionally the dead code elimination works correctly. So splitting into two if blocks is not necessary for correctness or meaningful size savings.

@vs-mobiletools-engineering-service2

This comment has been minimized.

rolfbjarne and others added 4 commits April 28, 2026 15:42
Both the 2-arg and 3-arg TypeMapAttribute constructors produced the same
auto-generated cache key (fullname + "::" + name), causing GetMethodReference
to return the wrong constructor. Fix by using explicit distinct cache keys
with parameter type suffixes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The linker's TypeMapHandler has a subtle interaction where the
TypeMapAssociationAttribute<SkippedObjectiveCTypeUniverse> for types
with generic variants (like NSOrderedSet/NSOrderedSet<T>) causes
MarkInstantiated to be called directly on the non-generic type,
bypassing MarkRequirementsForInstantiatedTypes. This poisons the
IsInstantiated flag, preventing ProcessType from ever being called
for these types, which means their TypeMapAttribute entries are
trimmed by the linker.

Fix this by using the 2-arg (unconditional) TypeMapAttribute constructor
for types that are the 'actual' target of SkippedObjectiveCTypeUniverse
associations. These entries are preserved whenever the group is seen
(i.e., when GetOrCreateExternalTypeMapping<NSObject>() is called).

Ref: dotnet/runtime#127504

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

@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.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@rolfbjarne
Copy link
Copy Markdown
Member Author

/review

@vs-mobiletools-engineering-service2
Copy link
Copy Markdown
Collaborator

✅ [PR Build #4133c8e] Build passed (Detect API changes) ✅

Pipeline on Agent
Hash: 4133c8ea799566f98c4ed31d990d9fcf6acf1998 [PR build]

@vs-mobiletools-engineering-service2
Copy link
Copy Markdown
Collaborator

✅ [PR Build #4133c8e] Build passed (Build packages) ✅

Pipeline on Agent
Hash: 4133c8ea799566f98c4ed31d990d9fcf6acf1998 [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: 4133c8ea799566f98c4ed31d990d9fcf6acf1998 [PR build]

@vs-mobiletools-engineering-service2
Copy link
Copy Markdown
Collaborator

✅ [CI Build #4133c8e] Build passed (Build macOS tests) ✅

Pipeline on Agent
Hash: 4133c8ea799566f98c4ed31d990d9fcf6acf1998 [PR build]

@vs-mobiletools-engineering-service2
Copy link
Copy Markdown
Collaborator

🔥 [CI Build #4133c8e] Test results 🔥

Test results

❌ Tests failed on VSTS: test results

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

Failures

❌ interdependent-binding-projects tests

2 tests failed, 2 tests passed.

Failed tests

  • interdependent-binding-projects/iOS - simulator/Debug: LaunchTimedOut
  • interdependent-binding-projects/tvOS - simulator/Debug: LaunchTimedOut

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
✅ introspection: All 6 tests passed. Html Report (VSDrops) Download
✅ linker: All 44 tests passed. Html Report (VSDrops) Download
✅ monotouch (iOS): All 13 tests passed. Html Report (VSDrops) Download
✅ monotouch (MacCatalyst): All 18 tests passed. Html Report (VSDrops) Download
✅ monotouch (macOS): All 18 tests passed. Html Report (VSDrops) Download
✅ monotouch (tvOS): All 13 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 Monterey (12): All 5 tests passed. Html Report (VSDrops) Download
✅ Tests on macOS Ventura (13): All 5 tests passed. Html Report (VSDrops) Download
✅ 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: 4133c8ea799566f98c4ed31d990d9fcf6acf1998 [PR build]

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement support for Interop Type Maps

5 participants