diff --git a/src/Controls/src/Build.Tasks/Controls.Build.Tasks.csproj b/src/Controls/src/Build.Tasks/Controls.Build.Tasks.csproj
index 811be6cbc4ab..579b3db17044 100644
--- a/src/Controls/src/Build.Tasks/Controls.Build.Tasks.csproj
+++ b/src/Controls/src/Build.Tasks/Controls.Build.Tasks.csproj
@@ -22,6 +22,11 @@
.NET Multi-platform App UI (.NET MAUI) is a cross-platform framework for creating native mobile and desktop apps with C# and XAML. This package only contains the MSBuild tasks and other tooling. Please install the Microsoft.Maui.Controls package to start using .NET MAUI.
+
+
+
+
+
@@ -34,6 +39,7 @@
+
@@ -50,6 +56,10 @@
+
+
+
+
diff --git a/src/Controls/src/Build.Tasks/MibcProfileGenerator/MibcProfileGenerator.csproj b/src/Controls/src/Build.Tasks/MibcProfileGenerator/MibcProfileGenerator.csproj
new file mode 100644
index 000000000000..fd9f0aff080d
--- /dev/null
+++ b/src/Controls/src/Build.Tasks/MibcProfileGenerator/MibcProfileGenerator.csproj
@@ -0,0 +1,32 @@
+
+
+
+ Exe
+ net10.0
+ MibcProfileGenerator
+ Microsoft.Maui.Controls.Build.Tasks
+ enable
+ enable
+ false
+ false
+
+
+
+ Generates MIBC profile files listing InitializeComponent methods from compiled MAUI assemblies.
+
+
+
+
+
+
+
+
+ <_CopyItems Include="$(TargetDir)MibcProfileGenerator.dll" />
+ <_CopyItems Include="$(TargetDir)MibcProfileGenerator.pdb" />
+ <_CopyItems Include="$(TargetDir)MibcProfileGenerator.runtimeconfig.json" />
+ <_CopyItems Include="$(TargetDir)MibcProfileGenerator.deps.json" />
+
+
+
+
+
diff --git a/src/Controls/src/Build.Tasks/MibcProfileGenerator/Program.cs b/src/Controls/src/Build.Tasks/MibcProfileGenerator/Program.cs
new file mode 100644
index 000000000000..c1c629e70a98
--- /dev/null
+++ b/src/Controls/src/Build.Tasks/MibcProfileGenerator/Program.cs
@@ -0,0 +1,458 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.IO.Compression;
+using System.Reflection;
+using System.Reflection.Metadata;
+using System.Reflection.Metadata.Ecma335;
+using System.Reflection.PortableExecutable;
+
+namespace Microsoft.Maui.Controls.Build.Tasks;
+
+///
+/// Generates a MIBC (Managed Instrumented Binary Code) profile file that lists all
+/// InitializeComponent* methods found in one or more input assemblies. MIBC files are
+/// consumed by crossgen2 and the .NET AOT compiler to guide profile-guided optimization.
+///
+/// The MAUI XAML processing pipeline generates several variants of InitializeComponent:
+/// - InitializeComponent — the primary entry point (all inflator modes)
+/// - InitializeComponentRuntime — runtime XAML inflation via LoadFromXaml (switch mode / HotReload fallback)
+/// - InitializeComponentXamlC — XamlC IL-compiled XAML inflation (switch mode)
+/// - InitializeComponentSourceGen — source-generator compiled XAML inflation (switch mode)
+/// All of these are included in the generated MIBC profile.
+///
+static class MibcProfileGenerator
+{
+ ///
+ /// Method name prefixes generated by the MAUI XAML toolchain.
+ /// See CodeBehindCodeWriter.cs and XamlCTask.cs for where these names are produced.
+ ///
+ static readonly string[] InitializeComponentMethodNames =
+ [
+ "InitializeComponent",
+ "InitializeComponentRuntime",
+ "InitializeComponentXamlC",
+ "InitializeComponentSourceGen",
+ ];
+
+ static int Main(string[] args)
+ {
+ if (args.Length < 2)
+ {
+ Console.Error.WriteLine("Usage: MibcProfileGenerator [ ...]");
+ Console.Error.WriteLine();
+ Console.Error.WriteLine("Scans input assemblies for InitializeComponent* methods and produces");
+ Console.Error.WriteLine("a MIBC profile file that can be consumed by crossgen2 (--mibc) or");
+ Console.Error.WriteLine("validated with 'dotnet pgo dump '.");
+ Console.Error.WriteLine();
+ Console.Error.WriteLine("Matched method names:");
+ foreach (var name in InitializeComponentMethodNames)
+ Console.Error.WriteLine($" - {name}");
+ return 1;
+ }
+
+ string outputPath = args[0];
+ string[] inputPaths = args.Skip(1).ToArray();
+ bool uncompressed = string.Equals(Path.GetExtension(outputPath), ".dll", StringComparison.OrdinalIgnoreCase);
+
+ var methods = new List();
+
+ foreach (string inputPath in inputPaths)
+ {
+ if (!File.Exists(inputPath))
+ {
+ Console.Error.WriteLine($"Error: Input file not found: {inputPath}");
+ return 1;
+ }
+
+ try
+ {
+ var discovered = DiscoverInitializeComponentMethods(inputPath);
+ methods.AddRange(discovered);
+ Console.WriteLine($"Found {discovered.Count} InitializeComponent* method(s) in {Path.GetFileName(inputPath)}");
+ }
+ catch (Exception ex)
+ {
+ Console.Error.WriteLine($"Error reading {inputPath}: {ex.Message}");
+ return 1;
+ }
+ }
+
+ if (methods.Count == 0)
+ {
+ Console.Error.WriteLine("Warning: No InitializeComponent* methods found in any input assembly.");
+ }
+
+ Console.WriteLine($"Total: {methods.Count} method(s) to emit into MIBC profile.");
+
+ try
+ {
+ EmitMibcFile(outputPath, methods, uncompressed);
+ Console.WriteLine($"Successfully wrote MIBC profile to {outputPath}");
+ }
+ catch (Exception ex)
+ {
+ Console.Error.WriteLine($"Error writing MIBC file: {ex.Message}");
+ return 1;
+ }
+
+ return 0;
+ }
+
+ ///
+ /// Represents a method discovered in an input assembly that should be included in the MIBC profile.
+ ///
+ sealed record DiscoveredMethod(
+ string AssemblyName,
+ Version? AssemblyVersion,
+ byte[]? PublicKeyToken,
+ string Namespace,
+ string TypeName,
+ string MethodName,
+ byte[] Signature);
+
+ ///
+ /// Scans an assembly for all InitializeComponent* methods generated by the MAUI XAML toolchain.
+ ///
+ static List DiscoverInitializeComponentMethods(string assemblyPath)
+ {
+ var results = new List();
+ var matchedNames = new HashSet(InitializeComponentMethodNames);
+
+ using var stream = File.OpenRead(assemblyPath);
+ using var peReader = new PEReader(stream);
+ var mdReader = peReader.GetMetadataReader();
+
+ // Get assembly identity
+ var assemblyDef = mdReader.GetAssemblyDefinition();
+ string asmName = mdReader.GetString(assemblyDef.Name);
+ Version? asmVersion = assemblyDef.Version;
+ byte[]? publicKeyToken = GetPublicKeyToken(mdReader, assemblyDef);
+
+ foreach (var typeHandle in mdReader.TypeDefinitions)
+ {
+ var typeDef = mdReader.GetTypeDefinition(typeHandle);
+ string typeName = mdReader.GetString(typeDef.Name);
+
+ // In ECMA-335, nested types have an empty Namespace; only the outermost
+ // declaring type carries the real namespace. Walk up to find it.
+ string ns = GetDeclaringNamespace(mdReader, typeDef);
+
+ // Handle nested types: walk up to build the full name with '/' separators
+ string fullTypeName = BuildFullTypeName(mdReader, typeDef, typeName);
+
+ foreach (var methodHandle in typeDef.GetMethods())
+ {
+ var methodDef = mdReader.GetMethodDefinition(methodHandle);
+ string methodName = mdReader.GetString(methodDef.Name);
+
+ if (!matchedNames.Contains(methodName))
+ continue;
+
+ // Read the raw signature blob
+ var sigBlob = mdReader.GetBlobBytes(methodDef.Signature);
+
+ results.Add(new DiscoveredMethod(
+ asmName,
+ asmVersion,
+ publicKeyToken,
+ ns,
+ fullTypeName,
+ methodName,
+ sigBlob));
+ }
+ }
+
+ return results;
+ }
+
+ ///
+ /// Gets the namespace for a type, walking up to the outermost declaring type for nested types.
+ /// In ECMA-335 metadata, nested TypeDefs have an empty Namespace field.
+ ///
+ static string GetDeclaringNamespace(MetadataReader mdReader, TypeDefinition typeDef)
+ {
+ var current = typeDef;
+ while (current.IsNested)
+ {
+ current = mdReader.GetTypeDefinition(current.GetDeclaringType());
+ }
+ return mdReader.GetString(current.Namespace);
+ }
+
+ ///
+ /// Builds the full type name including enclosing types for nested types, using '/' as separator.
+ ///
+ static string BuildFullTypeName(MetadataReader mdReader, TypeDefinition typeDef, string simpleName)
+ {
+ if (!typeDef.IsNested)
+ return simpleName;
+
+ var parts = new Stack();
+ parts.Push(simpleName);
+
+ var current = typeDef;
+ while (current.IsNested)
+ {
+ var declaringHandle = current.GetDeclaringType();
+ current = mdReader.GetTypeDefinition(declaringHandle);
+ parts.Push(mdReader.GetString(current.Name));
+ }
+
+ return string.Join("/", parts);
+ }
+
+ ///
+ /// Extracts the public key token from an assembly definition.
+ ///
+ static byte[]? GetPublicKeyToken(MetadataReader mdReader, AssemblyDefinition asmDef)
+ {
+ if (asmDef.PublicKey.IsNil)
+ return null;
+
+ byte[] publicKey = mdReader.GetBlobBytes(asmDef.PublicKey);
+ if (publicKey.Length == 0)
+ return null;
+
+ // If this is a full public key, convert to token via AssemblyName
+ if (publicKey.Length > 8)
+ {
+ var an = new AssemblyName();
+ an.SetPublicKey(publicKey);
+ return an.GetPublicKeyToken();
+ }
+
+ return publicKey;
+ }
+
+ ///
+ /// Emits a MIBC profile file in the standard format expected by crossgen2 / dotnet-pgo.
+ ///
+ /// The MIBC format is a PE assembly containing:
+ /// 1. A global method "AssemblyDictionary" that indexes groups by assembly name
+ /// 2. Per-assembly group methods that list profiled methods via ldtoken instructions
+ ///
+ /// For each profiled method the group method contains:
+ /// ldtoken [MemberRef for the method]
+ /// pop
+ ///
+ static void EmitMibcFile(string outputPath, List methods, bool uncompressed)
+ {
+ string assemblyName = Path.GetFileNameWithoutExtension(outputPath);
+
+ var mdBuilder = new MetadataBuilder();
+ var ilBuilder = new BlobBuilder();
+ var methodBodyStream = new MethodBodyStreamEncoder(ilBuilder);
+
+ // Module and assembly definition
+ var mvidBlob = mdBuilder.ReserveGuid();
+ mdBuilder.AddModule(
+ 0,
+ mdBuilder.GetOrAddString(assemblyName),
+ mvidBlob.Handle,
+ default, default);
+
+ mdBuilder.AddAssembly(
+ mdBuilder.GetOrAddString(assemblyName),
+ new Version(1, 0, 0, 0),
+ default,
+ default,
+ default,
+ AssemblyHashAlgorithm.None);
+
+ // type definition (required for global methods)
+ mdBuilder.AddTypeDefinition(
+ default,
+ default,
+ mdBuilder.GetOrAddString(""),
+ baseType: default,
+ fieldList: MetadataTokens.FieldDefinitionHandle(1),
+ methodList: MetadataTokens.MethodDefinitionHandle(1));
+
+ // Build the void() static method signature for global methods
+ var staticVoidSigBlob = new BlobBuilder();
+ new BlobEncoder(staticVoidSigBlob)
+ .MethodSignature(isInstanceMethod: false)
+ .Parameters(0, r => r.Void(), _ => { });
+ var staticVoidSigHandle = mdBuilder.GetOrAddBlob(staticVoidSigBlob);
+
+ // Cache of assembly references to avoid duplicates
+ var assemblyRefCache = new Dictionary();
+
+ // Cache of type references
+ var typeRefCache = new Dictionary();
+
+ // Create MemberRefs for all discovered methods and group by assembly
+ var groupedByAssembly = methods.GroupBy(m => m.AssemblyName).OrderBy(g => g.Key);
+ var groupMethods = new List<(string GroupName, MethodDefinitionHandle Handle)>();
+
+ int groupIndex = 0;
+ foreach (var group in groupedByAssembly)
+ {
+ groupIndex++;
+ string groupName = group.Key + ";";
+
+ // Emit one group method body with ldtoken/pop for each method
+ var groupIlBuf = new BlobBuilder();
+ var groupIl = new InstructionEncoder(groupIlBuf);
+
+ foreach (var method in group)
+ {
+ // Ensure assembly reference exists
+ if (!assemblyRefCache.TryGetValue(method.AssemblyName, out var asmRef))
+ {
+ AssemblyFlags flags = default;
+ BlobHandle pktBlob = default;
+ if (method.PublicKeyToken is { Length: > 0 })
+ {
+ pktBlob = mdBuilder.GetOrAddBlob(method.PublicKeyToken);
+ }
+
+ asmRef = mdBuilder.AddAssemblyReference(
+ mdBuilder.GetOrAddString(method.AssemblyName),
+ method.AssemblyVersion ?? new Version(0, 0, 0, 0),
+ default,
+ pktBlob,
+ flags,
+ default);
+ assemblyRefCache[method.AssemblyName] = asmRef;
+ }
+
+ // Build type reference (handle nested types)
+ var typeRef = GetOrAddTypeRef(mdBuilder, typeRefCache, asmRef, method.Namespace, method.TypeName);
+
+ // Create MemberRef for the method with its original signature
+ var memberRef = mdBuilder.AddMemberReference(
+ typeRef,
+ mdBuilder.GetOrAddString(method.MethodName),
+ mdBuilder.GetOrAddBlob(method.Signature));
+
+ // Emit: ldtoken , pop
+ groupIl.OpCode(ILOpCode.Ldtoken);
+ groupIl.Token(memberRef);
+ groupIl.OpCode(ILOpCode.Pop);
+ }
+
+ groupIl.OpCode(ILOpCode.Ret);
+
+ // Add the group method as a global method
+ int bodyOffset = methodBodyStream.AddMethodBody(groupIl, maxStack: 8);
+ string methodName = $"Assemblies_{group.Key}_{groupIndex}";
+ var groupMethodHandle = mdBuilder.AddMethodDefinition(
+ MethodAttributes.Public | MethodAttributes.Static,
+ MethodImplAttributes.IL,
+ mdBuilder.GetOrAddString(methodName),
+ staticVoidSigHandle,
+ bodyOffset,
+ default);
+
+ groupMethods.Add((groupName, groupMethodHandle));
+ }
+
+ // Emit AssemblyDictionary global method
+ var dictIlBuf = new BlobBuilder();
+ var dictIl = new InstructionEncoder(dictIlBuf);
+
+ foreach (var (groupName, groupMethodHandle) in groupMethods)
+ {
+ dictIl.LoadString(mdBuilder.GetOrAddUserString(groupName));
+ dictIl.OpCode(ILOpCode.Ldtoken);
+ dictIl.Token(groupMethodHandle);
+ dictIl.OpCode(ILOpCode.Pop);
+ }
+
+ dictIl.OpCode(ILOpCode.Ret);
+
+ int dictBodyOffset = methodBodyStream.AddMethodBody(dictIl, maxStack: 8);
+ mdBuilder.AddMethodDefinition(
+ MethodAttributes.Public | MethodAttributes.Static,
+ MethodImplAttributes.IL,
+ mdBuilder.GetOrAddString("AssemblyDictionary"),
+ staticVoidSigHandle,
+ dictBodyOffset,
+ default);
+
+ // Serialize to PE
+ var peBuilder = new ManagedPEBuilder(
+ new PEHeaderBuilder(),
+ new MetadataRootBuilder(mdBuilder),
+ ilBuilder,
+ deterministicIdProvider: content => new BlobContentId(
+ new Guid("97F4DBD4-F6D1-4FAD-91B3-1001F92068E5"), 0x04030201));
+
+ var peBlob = new BlobBuilder();
+ peBuilder.Serialize(peBlob);
+
+ // Write the MVID
+ new BlobWriter(mvidBlob.Content).WriteGuid(
+ new Guid("97F4DBD4-F6D1-4FAD-91B3-1001F92068E5"));
+
+ // Write output
+ if (uncompressed)
+ {
+ using var fs = new FileStream(outputPath, FileMode.Create);
+ peBlob.WriteContentTo(fs);
+ }
+ else
+ {
+ using var zip = ZipFile.Open(outputPath, ZipArchiveMode.Create);
+ var entry = zip.CreateEntry(Path.GetFileName(outputPath) + ".dll", CompressionLevel.Optimal);
+ using var entryStream = entry.Open();
+ peBlob.WriteContentTo(entryStream);
+ }
+ }
+
+ ///
+ /// Gets or creates a TypeReference handle, handling nested types (separated by '/').
+ ///
+ static TypeReferenceHandle GetOrAddTypeRef(
+ MetadataBuilder mdBuilder,
+ Dictionary cache,
+ AssemblyReferenceHandle asmRef,
+ string ns,
+ string typeName)
+ {
+ string cacheKey = $"{MetadataTokens.GetToken(asmRef):X}:{ns}.{typeName}";
+ if (cache.TryGetValue(cacheKey, out var existing))
+ return existing;
+
+ // Handle nested types: "Outer/Inner" -> TypeRef for Outer, then nested TypeRef for Inner
+ string[] parts = typeName.Split('/');
+ TypeReferenceHandle parentTypeRef = default;
+
+ for (int i = 0; i < parts.Length; i++)
+ {
+ string partName = parts[i];
+ string partKey;
+
+ if (i == 0)
+ {
+ partKey = $"{MetadataTokens.GetToken(asmRef):X}:{ns}.{partName}";
+ if (!cache.TryGetValue(partKey, out parentTypeRef))
+ {
+ parentTypeRef = mdBuilder.AddTypeReference(
+ asmRef,
+ mdBuilder.GetOrAddString(ns),
+ mdBuilder.GetOrAddString(partName));
+ cache[partKey] = parentTypeRef;
+ }
+ }
+ else
+ {
+ partKey = $"{MetadataTokens.GetToken(parentTypeRef):X}/{partName}";
+ if (!cache.TryGetValue(partKey, out var nestedRef))
+ {
+ nestedRef = mdBuilder.AddTypeReference(
+ parentTypeRef,
+ default,
+ mdBuilder.GetOrAddString(partName));
+ cache[partKey] = nestedRef;
+ }
+ parentTypeRef = nestedRef;
+ }
+ }
+
+ cache[cacheKey] = parentTypeRef;
+ return parentTypeRef;
+ }
+}
diff --git a/src/Controls/src/Build.Tasks/nuget/buildTransitive/netstandard2.0/Microsoft.Maui.Controls.targets b/src/Controls/src/Build.Tasks/nuget/buildTransitive/netstandard2.0/Microsoft.Maui.Controls.targets
index 0059c276f1a6..c4065314da3b 100644
--- a/src/Controls/src/Build.Tasks/nuget/buildTransitive/netstandard2.0/Microsoft.Maui.Controls.targets
+++ b/src/Controls/src/Build.Tasks/nuget/buildTransitive/netstandard2.0/Microsoft.Maui.Controls.targets
@@ -254,6 +254,33 @@
+
+ <_MauiMibcProfilePath>$(IntermediateOutputPath)$(TargetName).mibc
+
+
+
+
+
+ <_MibcProfileGeneratorPath>$(MSBuildThisFileDirectory)MibcProfileGenerator.dll
+
+
+
+
+
+
+
+
+
+
+
+
+