Skip to content

Add MIBC profile generator for InitializeComponent methods#34660

Open
jkoritzinsky wants to merge 2 commits intodotnet:mainfrom
jkoritzinsky:feature/mibc-profile-generator
Open

Add MIBC profile generator for InitializeComponent methods#34660
jkoritzinsky wants to merge 2 commits intodotnet:mainfrom
jkoritzinsky:feature/mibc-profile-generator

Conversation

@jkoritzinsky
Copy link
Member

@jkoritzinsky jkoritzinsky commented Mar 25, 2026

Summary

Adds a build tool that generates MIBC (Managed Instrumented Binary Code) profile files listing all InitializeComponent* methods from compiled MAUI assemblies. These files do not have any actual profile data, but they can be consumed by crossgen2 for partial compilation. Also add MSBuild targets to enable partial R2R for just XAML-generated code in Debug builds for CoreCLR targets using said tool to provide a faster experience for XAML loading in Debug using said MIBC files.

What's included

MibcProfileGenerator tool (src/Controls/src/Build.Tasks/MibcProfileGenerator/)

A standalone .NET console app that:

  • Reads one or more input assemblies using \System.Reflection.Metadata\
  • Discovers all XAML-generated method variants:
    • InitializeComponent — primary entry point
    • InitializeComponentRuntime — runtime XAML inflation
    • InitializeComponentXamlC — XamlC IL-compiled path
    • InitializeComponentSourceGen — source generator compiled path
  • Emits a valid MIBC PE assembly following the format from dotnet/runtime's MibcEmitter.cs
  • Supports both compressed (.mibc) and uncompressed (.dll) output
  • Handles nested types, multiple assemblies, and proper metadata references
  • Validated with dotnet-pgo dump

MSBuild integration

  • New opt-in target _MauiGenerateMibcProfile in Microsoft.Maui.Controls.targets
  • Runs after XamlC so all IL post-processing is complete
  • Enabled by in Debug CoreCLR builds.
  • Incremental build support via Inputs/Outputs

Packaging

  • Tool is packaged alongside existing build tasks in the NuGet package
  • Local dev support via _CopyToBuildTasksDir target

Usage

Automatic in Debug CoreCLR builds.

Add a build tool that scans compiled MAUI assemblies for all
InitializeComponent* methods (InitializeComponent, InitializeComponentRuntime,
InitializeComponentXamlC, InitializeComponentSourceGen) and produces a MIBC
profile file consumable by crossgen2 and the .NET AOT compiler for PGO.

The tool is integrated into the MSBuild pipeline via the
_MauiGenerateMibcProfile target, which runs after XamlC and is opt-in
via the MauiGenerateMibcProfile property. The generated file is exposed
as the @(MauiMibcProfile) MSBuild item for downstream consumption.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions
Copy link
Contributor

github-actions bot commented Mar 25, 2026

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 34660

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 34660"

@dotnet-policy-service dotnet-policy-service bot added the community ✨ Community Contribution label Mar 25, 2026
@dotnet-policy-service
Copy link
Contributor

Hey there @@jkoritzinsky! Thank you so much for your PR! Someone from the team will get assigned to your PR shortly and we'll get it reviewed.

@jkoritzinsky jkoritzinsky marked this pull request as ready for review March 25, 2026 22:52
Copilot AI review requested due to automatic review settings March 25, 2026 22:52
Copy link
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

Adds a new build-time tool and MSBuild integration to generate a MIBC profile containing all InitializeComponent* methods from compiled MAUI assemblies, intended to enable partial R2R for XAML-generated code paths.

Changes:

  • Add MibcProfileGenerator console tool that scans assemblies via System.Reflection.Metadata and emits a MIBC PE (optionally compressed).
  • Add _MauiGenerateMibcProfile MSBuild target to run the tool after XamlC and feed the output into PublishReadyToRunPgoFiles / crossgen2 --partial.
  • Package the tool alongside existing Controls build tasks for transitive consumption.

Reviewed changes

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

File Description
src/Controls/src/Build.Tasks/nuget/buildTransitive/netstandard2.0/Microsoft.Maui.Controls.targets Adds opt-in target to generate a .mibc and adjust crossgen2 args for partial compilation.
src/Controls/src/Build.Tasks/MibcProfileGenerator/Program.cs Implements the assembly scanner and MIBC emitter.
src/Controls/src/Build.Tasks/MibcProfileGenerator/MibcProfileGenerator.csproj New net10.0 tool project definition + local copy target.
src/Controls/src/Build.Tasks/Controls.Build.Tasks.csproj Wires tool into build tasks packaging (adds project reference + packed outputs).

- Add explicit ret instructions to group and AssemblyDictionary IL methods
- Fix nested type namespace resolution (walk to outermost declaring type)
- Include deps.json in NuGet package and local dev copy
- Fix comment to match actual property/item names
- Fix indentation to use tabs consistently in targets file

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Runs after XamlC so the IL has been post-processed and method signatures are final.
Produces a $(_MauiMibcProfilePath) file and adds it to the @(PublishReadyToRunPgoFiles) item. -->
<Target Name="_MauiGenerateMibcProfile"
AfterTargets="XamlC"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The XamlC target should not run eventually (hopefully already in .NET 11) and XAML code should be compiled just via the source generator by default. In that case, we should make sure this runs after CoreCompile instead.

Comment on lines +281 to +283
<PropertyGroup>
<PublishReadyToRunCrossgen2ExtraArgs>$(PublishReadyToRunCrossgen2ExtraArgs) --partial</PublishReadyToRunCrossgen2ExtraArgs>
</PropertyGroup>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a different spot in the MAUI SDK which turns on partial R2R - it's the default whenever R2R is enabled (which is also the default). I don't think we should enforce partial here and allow devs to disable it via $(_MauiPublishReadyToRunPartial)=false. Maybe we could actually use that property to skip this task.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@simonrozsival where is that? I only see that in src/Workload/Microsoft.Maui.Sdk/Sdk/Microsoft.Maui.Sdk.Before.targets which only works for Windows today and doesn't turn on partial R2R.

<_MibcProfileGeneratorPath>$(MSBuildThisFileDirectory)MibcProfileGenerator.dll</_MibcProfileGeneratorPath>
</PropertyGroup>

<Exec Command="dotnet exec &quot;$(_MibcProfileGeneratorPath)&quot; &quot;$(_MauiMibcProfilePath)&quot; &quot;$(IntermediateOutputPath)$(TargetFileName)&quot;" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder why we explicitly dotnet exec here instead of turning this into a custom MSBuild task?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

System.Reflection.Metadata is a very finicky dependency to get right for MSBuild. Also having a separate tool enables me (or rather Copilot) to more easily validate the tool with ad-hoc testing.

Comment on lines +33 to +35
"InitializeComponentRuntime",
"InitializeComponentXamlC",
"InitializeComponentSourceGen",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In Release builds, only the InitializeComponent method is relevant. The other 3 are used only for unit testing, when we need to generate all 3 versions at build time and switch between them at runtime.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unit testing, and full page XamlHotReload. But as @simonrozsival said, in Release builds, only InitializeComponent should be present

@simonrozsival
Copy link
Member

@jkoritzinsky I really like this. I think this is a great start and once we have this task that generates an app-specific profile based on static analysis, we can experiment with including more than just InitializeComponent methods in the .mibc.

For example, we were thinking about changing the way we generate the "inflator" code to be split between smaller methods (https://github.com/dotnet/maui/pull/31555/changes#diff-d1e4f87785dfbad698ce654733169bf7e6091581ccebdbada7f5e66460b17bfcR166-R224) which allowed us to generate much more efficient code which showed in benchmarks. If we wanted to do that, we should also update MibcProfileGenerator.

One other thing we discussed internally some time ago was that maybe we should simply R2R anything in the app-specific .dlls. They are usually quite small compared to the BCL + MAUI SDK and Android/iOS bindings, so just naively compiling everything should not increase the app bundle too much and it would make sure we don't need to JIT any of the app code at startup.

@vitek-karas
Copy link
Member

The description of this PR might be a bit misleading since it says it's for Debug builds. If I read the code correctly it will work for both. In reality this doesn't make much sense for Debug builds - we will not R2R the application in Debug builds because it would hurt built time, and I think the cost of running crossgen is larger than the startup gain it provides. Additionally, we don't have the ability to debug R2R code, so we can't really R2R the app itself anyway.

For Release builds this would make a lot of sense. The prime example of code this can help with is XAML inflation - which is the InitializeComponents. But as Simon mentions above the shape of that code will evolve, we want to make it less of a single large method. Also in .NET 11 the default is source generated, so XamlC should not be used at all.

XAML inflation is responsibility of the XSG (XAML Source Gen), thus that's the component with the best knowledge of what methods would benefit from being R2R. Personally, I think it would be even better if we could introduce some attribute which XSG puts on certain methods and that would tell this new tool to include those methods in the MIBC file. @StephaneDelcroix what do you think?

@StephaneDelcroix
Copy link
Contributor

XAML inflation is responsibility of the XSG (XAML Source Gen), thus that's the component with the best knowledge of what methods would benefit from being R2R. Personally, I think it would be even better if we could introduce some attribute which XSG puts on certain methods and that would tell this new tool to include those methods in the MIBC file. @StephaneDelcroix what do you think?

Thanks for this, I love what you're doing there

a few thoughts:

  • it should only be done for Release builds. With Debug builds, we aim for the lowest build time possible, and make the develop as fast possible, so extra analysis, and r2r, doesn't make much sense there
  • whatever the inflator you opt for (it's still an option to ask, even in Release builds, for Runtime, or XamlC inflation), the only method present should be InitializeComponent for Release builds. if it's not, it's a bug that we should fix
  • at this time, and for net11.0, the source generator only generates a single, large, InitializeComponent, but the compiler might generate extra types and methods (for lambda). As @vitek-karas said, it might make sense to provide an attribute, like MethodImplAttribute is doing (but not quite) so we, and eventually the user, have a way to tag those methods.
  • if we do so, we might need diagnostics reports about the consequences of so many R2R methods...

@jkoritzinsky
Copy link
Member Author

The idea of this tool is to run only in Debug builds.

@vitek-karas this tool only makes sense for Debug builds, as for Release these methods should already be included in R2R unless MAUI's existing partial R2R support doesn't include the user's app assembly or the XAML code. If the Release configuration currently doesn't include the XAML code and we want it to do so, then this becomes interesting for Release.

@StephaneDelcroix is our goal for the Debug experience lowest build time or lowest F5 (build time + startup time to first page)? This PR is meant to improve the overall F5 time by spending a little more time in build to get a lot faster startup. If that's not what we want to optimize for (or this tool does not achieve it) then it's not worth adding.

I'll update this tool to run after CoreCompile as well to catch the debug scenario.

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

Labels

community ✨ Community Contribution

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants