From 75c82fd6a7e600ae78bc98b8ce2dfb29abe9ccd0 Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Tue, 15 Sep 2026 23:56:27 -0500 Subject: [PATCH 1/2] Allow intrinsic roots in partial no-JIT compilations Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../WasmArgumentLayoutTests.cs | 58 +++++++++++++++++-- ...RunSingleAssemblyCompilationModuleGroup.cs | 4 +- 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/WasmArgumentLayoutTests.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/WasmArgumentLayoutTests.cs index f510b504affc6a..1bd323d10682a5 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/WasmArgumentLayoutTests.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/WasmArgumentLayoutTests.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis.Emit; using crossgen2::ILCompiler; +using crossgen2::ILCompiler.IBC; using crossgen2::ILCompiler.DependencyAnalysis.ReadyToRun; using crossgen2::ILCompiler.DependencyAnalysis.Wasm; using crossgen2::ILCompiler.PortableCallHelpers; @@ -74,6 +75,49 @@ public static TheoryData V128Types() return data; } + [Theory] + [InlineData(false, true)] + [InlineData(true, false)] + public void PartialCompilationWithEmptyProfileIncludesRequiredHardwareIntrinsics( + bool targetAllowsRuntimeCodeGeneration, + bool expectedIntrinsicIncluded) + { + (ReadyToRunCompilerContext context, ReadyToRunSingleAssemblyCompilationModuleGroup compilationGroup) = + CreateWasmContext(targetAllowsRuntimeCodeGeneration); + EcmaModule coreLib = (EcmaModule)context.SystemModule; + var profileDataManager = new ProfileDataManager( + Logger.Null, + new ModuleDesc[] { coreLib }, + new ModuleDesc[] { coreLib }, + new ModuleDesc[] { coreLib }, + Array.Empty(), + nonLocalGenericsHome: null, + Array.Empty(), + MIbcProfileParser.MibcGroupParseRules.VersionBubbleWithCrossModule1, + callChainProfile: null, + context, + compilationGroup, + embedPgoDataInR2RImage: false, + parseIbcData: false, + compilationGroup.VersionsWithMethodBody, + synthesizeRandomPgoData: false); + compilationGroup.ApplyProfileGuidedOptimizationData(profileDataManager, partial: true); + + MetadataType[] intrinsicTypes = + [ + context.SystemModule.GetType("System.Runtime.Intrinsics.Wasm"u8, "PackedSimd"u8), + context.SystemModule.GetType("System.Runtime.Intrinsics.Wasm"u8, "WasmBase"u8), + ]; + MethodDesc nonIntrinsic = context.GetWellKnownType(WellKnownType.Object).GetMethod("ToString"u8, null); + + foreach (MetadataType intrinsicType in intrinsicTypes) + { + MethodDesc intrinsic = intrinsicType.GetMethod("get_IsSupported"u8, null); + Assert.Equal(expectedIntrinsicIncluded, compilationGroup.ContainsMethodBody(intrinsic, unboxingStub: false)); + } + Assert.False(compilationGroup.ContainsMethodBody(nonIntrinsic, unboxingStub: false)); + } + /// /// Every type the wasm ABI passes as a v128 must be 16-byte aligned. Both this compiler /// and the runtime align an argument slot by the argument type's own alignment, so an @@ -744,6 +788,10 @@ private static EcmaType GetSystemType(ReadyToRunCompilerContext context, string /// app closure, which a real build always supplies alongside CoreLib. /// private ReadyToRunCompilerContext CreateWasmContext(params string[] extraInputAssemblyPaths) + => CreateWasmContext(targetAllowsRuntimeCodeGeneration: false, extraInputAssemblyPaths).Context; + + private (ReadyToRunCompilerContext Context, ReadyToRunSingleAssemblyCompilationModuleGroup CompilationGroup) + CreateWasmContext(bool targetAllowsRuntimeCodeGeneration, params string[] extraInputAssemblyPaths) { string coreLibPath = TestPaths.SystemPrivateCoreLibPath; Assert.True(File.Exists(coreLibPath), $"System.Private.CoreLib.dll not found at '{coreLibPath}'"); @@ -757,8 +805,7 @@ private ReadyToRunCompilerContext CreateWasmContext(params string[] extraInputAs inputFilePaths.Add(Path.GetFileNameWithoutExtension(path), path); } - // Wasm cannot generate code at runtime, matching what crossgen2's Program computes for this target. - ReadyToRunCompilerContext context = new(target, SharedGenericsMode.CanonicalReferenceTypes, bubbleIncludesCoreModule: true, targetAllowsRuntimeCodeGeneration: false, instructionSetSupport, oldTypeSystemContext: null) + ReadyToRunCompilerContext context = new(target, SharedGenericsMode.CanonicalReferenceTypes, bubbleIncludesCoreModule: true, targetAllowsRuntimeCodeGeneration, instructionSetSupport, oldTypeSystemContext: null) { InputFilePaths = inputFilePaths, ReferenceFilePaths = new Dictionary(StringComparer.OrdinalIgnoreCase), @@ -769,7 +816,7 @@ private ReadyToRunCompilerContext CreateWasmContext(params string[] extraInputAs // The R2R field layout algorithm reaches into the compilation group to decide whether base // offsets need aligning, so a context without one throws before computing any layout. - context.SetCompilationGroup(new ReadyToRunSingleAssemblyCompilationModuleGroup(new ReadyToRunCompilationModuleGroupConfig + var compilationGroup = new ReadyToRunSingleAssemblyCompilationModuleGroup(new ReadyToRunCompilationModuleGroupConfig { Context = context, IsInputBubble = true, @@ -777,9 +824,10 @@ private ReadyToRunCompilerContext CreateWasmContext(params string[] extraInputAs VersionBubbleModuleSet = new ModuleDesc[] { coreLib }, CrossModuleInlineable = Array.Empty(), InstructionSetSupport = instructionSetSupport, - })); + }); + context.SetCompilationGroup(compilationGroup); - return context; + return (context, compilationGroup); } private static DefType InstantiateVector(ReadyToRunCompilerContext context, string vectorType, WellKnownType elementType) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunSingleAssemblyCompilationModuleGroup.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunSingleAssemblyCompilationModuleGroup.cs index 71450f6d12487e..add2cbc8e43838 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunSingleAssemblyCompilationModuleGroup.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunSingleAssemblyCompilationModuleGroup.cs @@ -29,7 +29,9 @@ protected sealed override bool ContainsMethodBodyCore(MethodDesc method, bool un if (_profileGuidedCompileRestriction != null) { - if (!_profileGuidedCompileRestriction.IsMethodInInputProfileData(method)) + if (!_profileGuidedCompileRestriction.IsMethodInInputProfileData(method) && + (((ReadyToRunCompilerContext)method.Context).TargetAllowsRuntimeCodeGeneration || + !HardwareIntrinsicHelpers.IsHardwareIntrinsic(method))) return false; } From a0b21a9a4b93c0d2939fc9d828020d416521c893 Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Wed, 16 Sep 2026 10:37:05 -0500 Subject: [PATCH 2/2] Remove redundant intrinsic profile unit test Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../WasmArgumentLayoutTests.cs | 58 ++----------------- 1 file changed, 5 insertions(+), 53 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/WasmArgumentLayoutTests.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/WasmArgumentLayoutTests.cs index 1bd323d10682a5..f510b504affc6a 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/WasmArgumentLayoutTests.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/WasmArgumentLayoutTests.cs @@ -13,7 +13,6 @@ using Microsoft.CodeAnalysis.Emit; using crossgen2::ILCompiler; -using crossgen2::ILCompiler.IBC; using crossgen2::ILCompiler.DependencyAnalysis.ReadyToRun; using crossgen2::ILCompiler.DependencyAnalysis.Wasm; using crossgen2::ILCompiler.PortableCallHelpers; @@ -75,49 +74,6 @@ public static TheoryData V128Types() return data; } - [Theory] - [InlineData(false, true)] - [InlineData(true, false)] - public void PartialCompilationWithEmptyProfileIncludesRequiredHardwareIntrinsics( - bool targetAllowsRuntimeCodeGeneration, - bool expectedIntrinsicIncluded) - { - (ReadyToRunCompilerContext context, ReadyToRunSingleAssemblyCompilationModuleGroup compilationGroup) = - CreateWasmContext(targetAllowsRuntimeCodeGeneration); - EcmaModule coreLib = (EcmaModule)context.SystemModule; - var profileDataManager = new ProfileDataManager( - Logger.Null, - new ModuleDesc[] { coreLib }, - new ModuleDesc[] { coreLib }, - new ModuleDesc[] { coreLib }, - Array.Empty(), - nonLocalGenericsHome: null, - Array.Empty(), - MIbcProfileParser.MibcGroupParseRules.VersionBubbleWithCrossModule1, - callChainProfile: null, - context, - compilationGroup, - embedPgoDataInR2RImage: false, - parseIbcData: false, - compilationGroup.VersionsWithMethodBody, - synthesizeRandomPgoData: false); - compilationGroup.ApplyProfileGuidedOptimizationData(profileDataManager, partial: true); - - MetadataType[] intrinsicTypes = - [ - context.SystemModule.GetType("System.Runtime.Intrinsics.Wasm"u8, "PackedSimd"u8), - context.SystemModule.GetType("System.Runtime.Intrinsics.Wasm"u8, "WasmBase"u8), - ]; - MethodDesc nonIntrinsic = context.GetWellKnownType(WellKnownType.Object).GetMethod("ToString"u8, null); - - foreach (MetadataType intrinsicType in intrinsicTypes) - { - MethodDesc intrinsic = intrinsicType.GetMethod("get_IsSupported"u8, null); - Assert.Equal(expectedIntrinsicIncluded, compilationGroup.ContainsMethodBody(intrinsic, unboxingStub: false)); - } - Assert.False(compilationGroup.ContainsMethodBody(nonIntrinsic, unboxingStub: false)); - } - /// /// Every type the wasm ABI passes as a v128 must be 16-byte aligned. Both this compiler /// and the runtime align an argument slot by the argument type's own alignment, so an @@ -788,10 +744,6 @@ private static EcmaType GetSystemType(ReadyToRunCompilerContext context, string /// app closure, which a real build always supplies alongside CoreLib. /// private ReadyToRunCompilerContext CreateWasmContext(params string[] extraInputAssemblyPaths) - => CreateWasmContext(targetAllowsRuntimeCodeGeneration: false, extraInputAssemblyPaths).Context; - - private (ReadyToRunCompilerContext Context, ReadyToRunSingleAssemblyCompilationModuleGroup CompilationGroup) - CreateWasmContext(bool targetAllowsRuntimeCodeGeneration, params string[] extraInputAssemblyPaths) { string coreLibPath = TestPaths.SystemPrivateCoreLibPath; Assert.True(File.Exists(coreLibPath), $"System.Private.CoreLib.dll not found at '{coreLibPath}'"); @@ -805,7 +757,8 @@ private ReadyToRunCompilerContext CreateWasmContext(params string[] extraInputAs inputFilePaths.Add(Path.GetFileNameWithoutExtension(path), path); } - ReadyToRunCompilerContext context = new(target, SharedGenericsMode.CanonicalReferenceTypes, bubbleIncludesCoreModule: true, targetAllowsRuntimeCodeGeneration, instructionSetSupport, oldTypeSystemContext: null) + // Wasm cannot generate code at runtime, matching what crossgen2's Program computes for this target. + ReadyToRunCompilerContext context = new(target, SharedGenericsMode.CanonicalReferenceTypes, bubbleIncludesCoreModule: true, targetAllowsRuntimeCodeGeneration: false, instructionSetSupport, oldTypeSystemContext: null) { InputFilePaths = inputFilePaths, ReferenceFilePaths = new Dictionary(StringComparer.OrdinalIgnoreCase), @@ -816,7 +769,7 @@ private ReadyToRunCompilerContext CreateWasmContext(params string[] extraInputAs // The R2R field layout algorithm reaches into the compilation group to decide whether base // offsets need aligning, so a context without one throws before computing any layout. - var compilationGroup = new ReadyToRunSingleAssemblyCompilationModuleGroup(new ReadyToRunCompilationModuleGroupConfig + context.SetCompilationGroup(new ReadyToRunSingleAssemblyCompilationModuleGroup(new ReadyToRunCompilationModuleGroupConfig { Context = context, IsInputBubble = true, @@ -824,10 +777,9 @@ private ReadyToRunCompilerContext CreateWasmContext(params string[] extraInputAs VersionBubbleModuleSet = new ModuleDesc[] { coreLib }, CrossModuleInlineable = Array.Empty(), InstructionSetSupport = instructionSetSupport, - }); - context.SetCompilationGroup(compilationGroup); + })); - return (context, compilationGroup); + return context; } private static DefType InstantiateVector(ReadyToRunCompilerContext context, string vectorType, WellKnownType elementType)