From fa59f6d7b27a44deafed1085a781817bf8f22c87 Mon Sep 17 00:00:00 2001
From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com>
Date: Sat, 18 Jul 2026 18:09:21 -0700
Subject: [PATCH 01/17] Use Wasm runtime pack for building wasm in
ILCompiler.ReadyToRun.Tests
---
eng/pipelines/runtime.yml | 45 +++++
.../ILCompiler.ReadyToRun.Tests.csproj | 51 +++++-
.../TestCases/R2RTestSuites.cs | 11 +-
.../TestCasesRunner/R2RResultChecker.cs | 18 +-
.../TestCasesRunner/R2RTestCaseCompiler.cs | 24 +--
.../TestCasesRunner/R2RTestRunner.cs | 94 +++++++---
.../TestCasesRunner/TestPaths.cs | 169 +++---------------
...rosoft.NETCore.App.Runtime.CoreCLR.sfxproj | 2 +-
8 files changed, 207 insertions(+), 207 deletions(-)
diff --git a/eng/pipelines/runtime.yml b/eng/pipelines/runtime.yml
index 77ec17437b8a1e..a122b1b40d0829 100644
--- a/eng/pipelines/runtime.yml
+++ b/eng/pipelines/runtime.yml
@@ -861,9 +861,24 @@ extends:
jobParameters:
timeoutInMinutes: 120
nameSuffix: CLR_Tools_Tests
+ # Depend on the browser-wasm libraries built below so the wasm crossgen2 R2R test
+ # (ILCompiler.ReadyToRun.Tests) can compile against real browser-wasm references.
+ dependsOn:
+ - build_browser_wasm_linux_Release_CLR_Tools_Tests_WasmLibs
buildArgs: -s clr.aot+clr.iltools+clr.corelib+clr.nativecorelib+libs.sfx+clr.toolstests+tools.cdac+tools.cdactests -c $(_BuildConfig) -test
enablePublishTestResults: true
testResultsFormat: 'xunit'
+ preBuildSteps:
+ # Lay the browser-wasm libraries into artifacts/bin so TestPaths' WasmRuntimePackDir
+ # resolves and the wasm R2R test compiles against real browser-wasm references.
+ - template: /eng/pipelines/common/download-artifact-step.yml
+ parameters:
+ artifactName: CLR_Tools_Tests_WasmLibs_BuildArtifacts_browser_wasm_Release
+ artifactFileName: CLR_Tools_Tests_WasmLibs_BuildArtifacts_browser_wasm_Release$(archiveExtension)
+ unpackFolder: $(Build.SourcesDirectory)/artifacts/bin
+ cleanUnpackFolder: false
+ overwriteExistingFiles: true
+ displayName: Browser-Wasm Libraries
# We want to run AOT tests when illink changes because there's share code and tests from illink which are used by AOT
condition: >-
or(
@@ -871,6 +886,36 @@ extends:
eq(stageDependencies.EvaluatePaths.evaluate_paths.outputs['SetPathVars_tools_illink.containsChange'], true),
eq(stageDependencies.EvaluatePaths.evaluate_paths.outputs['SetPathVars_tools_cdac.containsChange'], true),
eq(variables['isRollingBuild'], true))
+
+ # Build the browser-wasm managed runtime pack that the CLR_Tools_Tests job uses
+ # as crossgen2 references for the wasm ReadyToRun test. Native runtime and host
+ # assets are omitted because the test only consumes managed assemblies.
+ - template: /eng/pipelines/common/platform-matrix.yml
+ parameters:
+ jobTemplate: /eng/pipelines/common/global-build-job.yml
+ buildConfig: Release
+ platforms:
+ - browser_wasm
+ jobParameters:
+ nameSuffix: CLR_Tools_Tests_WasmLibs
+ buildArgs: -s clr.corelib+libs+libs.pretest -c Release /p:IncludeStaticLibrariesInPack=false
+ timeoutInMinutes: 120
+ postBuildSteps:
+ - template: /eng/pipelines/common/upload-artifact-step.yml
+ parameters:
+ rootFolder: $(Build.SourcesDirectory)/artifacts/bin
+ includeRootFolder: false
+ archiveType: $(archiveType)
+ archiveExtension: $(archiveExtension)
+ tarCompression: $(tarCompression)
+ artifactName: CLR_Tools_Tests_WasmLibs_BuildArtifacts_$(osGroup)$(osSubgroup)_$(archType)_$(_BuildConfig)
+ displayName: Browser-Wasm Libraries
+ condition: >-
+ or(
+ eq(stageDependencies.EvaluatePaths.evaluate_paths.outputs['SetPathVars_coreclr.containsChange'], true),
+ eq(stageDependencies.EvaluatePaths.evaluate_paths.outputs['SetPathVars_tools_illink.containsChange'], true),
+ eq(stageDependencies.EvaluatePaths.evaluate_paths.outputs['SetPathVars_tools_cdac.containsChange'], true),
+ eq(variables['isRollingBuild'], true))
#
# Build CrossDacs
#
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj
index e3f0ab6ea0cd15..14cec02ab0230e 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj
@@ -12,6 +12,12 @@
Debug;Release;Checked
true
-notrait category=failing
+ false
+ $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'microsoft.netcore.app.runtime.browser-wasm'))
+ $([MSBuild]::NormalizeDirectory('$(WasmRuntimePackRoot)', '$(LibrariesConfiguration)', 'runtimes', 'browser-wasm', 'lib', '$(NetCoreAppCurrent)'))
+ $([MSBuild]::NormalizeDirectory('$(WasmRuntimePackRoot)', '$(LibrariesConfiguration)', 'runtimes', 'browser-wasm', 'native'))
+ $([MSBuild]::NormalizeDirectory('$(WasmRuntimePackRoot)', 'Release', 'runtimes', 'browser-wasm', 'lib', '$(NetCoreAppCurrent)'))
+ $([MSBuild]::NormalizeDirectory('$(WasmRuntimePackRoot)', 'Release', 'runtimes', 'browser-wasm', 'native'))
@@ -72,6 +78,25 @@
SkipUnchangedFiles="true" />
+
+
+
+
+
+
+
+
+
+
+
@@ -81,6 +106,7 @@
+
$(CoreCLRArtifactsPath)/$(BuildArchitecture)/crossgen2
@@ -90,18 +116,25 @@
$(MicrosoftNetCoreAppRuntimePackNativeDir)
-
- $(MicrosoftNetCoreAppRefPackRefDir)
-
-
- $(CoreCLRArtifactsPath)
+
+
+
+ $(RequireWasmReferences)
-
- $(TargetArchitecture)
+
+ $(WasmRuntimePackDir)
-
- $(TargetOS)
+
+ $(WasmRuntimePackNativeDir)
+
+
$(CoreCLRConfiguration)
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs
index e29a57c61cccc2..8750b3551ff007 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs
@@ -76,13 +76,10 @@ public void WasmWebcilModule()
new(nameof(WasmWebcilModule), [new CrossgenAssembly(wasmWebcilModule)])
{
OutputFileExtension = ".wasm",
- AdditionalArgs =
- {
- "--targetarch",
- "wasm",
- "--targetos",
- "browser",
- },
+ // Reference the real browser-wasm framework assemblies when they are available
+ // (e.g. the wasm tools-test job downloads them); otherwise the runner falls back
+ // to host references so this test still runs everywhere.
+ TargetRid = TargetRid.BrowserWasm,
Validate = Validate,
},
]));
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RResultChecker.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RResultChecker.cs
index 33b4ed1d743516..f7f11d6f5c5369 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RResultChecker.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RResultChecker.cs
@@ -1194,11 +1194,15 @@ public static bool MethodILIsPresent(string msilFilePath, string declaringType,
///
internal sealed class SimpleAssemblyResolver : IAssemblyResolver
{
- private readonly TestPaths _paths;
+ private readonly Dictionary _assemblyPaths;
- public SimpleAssemblyResolver(TestPaths paths)
+ public SimpleAssemblyResolver(IEnumerable referencePaths)
{
- _paths = paths;
+ _assemblyPaths = new Dictionary(StringComparer.OrdinalIgnoreCase);
+ foreach (string referencePath in referencePaths)
+ {
+ _assemblyPaths[Path.GetFileNameWithoutExtension(referencePath)] = referencePath;
+ }
}
public IAssemblyMetadata? FindAssembly(MetadataReader metadataReader, AssemblyReferenceHandle assemblyReferenceHandle, string parentFile)
@@ -1217,10 +1221,12 @@ public SimpleAssemblyResolver(TestPaths paths)
string candidate = Path.Combine(dir, simpleName + ".dll");
if (!File.Exists(candidate))
- candidate = Path.Combine(_paths.RuntimePackDir, simpleName + ".dll");
+ {
+ if (!_assemblyPaths.TryGetValue(simpleName, out string? referencePath) || referencePath is null)
+ return null;
- if (!File.Exists(candidate))
- return null;
+ candidate = referencePath;
+ }
return new SimpleAssemblyMetadata(candidate);
}
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestCaseCompiler.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestCaseCompiler.cs
index 7bd12c8887fe59..cffccdf5c65b34 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestCaseCompiler.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestCaseCompiler.cs
@@ -9,6 +9,7 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Emit;
+using Xunit.Abstractions;
namespace ILCompiler.ReadyToRun.Tests.TestCasesRunner;
@@ -19,26 +20,13 @@ internal sealed class R2RTestCaseCompiler
{
private readonly List _frameworkReferences;
- public R2RTestCaseCompiler(TestPaths paths)
+ public R2RTestCaseCompiler(List references)
{
_frameworkReferences = new List();
- // Add reference assemblies from the ref pack (needed for Roslyn compilation)
- string refPackDir = paths.RefPackDir;
- if (Directory.Exists(refPackDir))
+ foreach (string refPath in references)
{
- foreach (string refPath in Directory.EnumerateFiles(refPackDir, "*.dll"))
- {
- _frameworkReferences.Add(MetadataReference.CreateFromFile(refPath));
- }
- }
- else
- {
- // Fallback to runtime pack implementation assemblies
- foreach (string refPath in paths.GetFrameworkReferencePaths())
- {
- _frameworkReferences.Add(MetadataReference.CreateFromFile(refPath));
- }
+ _frameworkReferences.Add(MetadataReference.CreateFromFile(refPath));
}
}
@@ -85,7 +73,7 @@ public string CompileAssembly(
syntaxTrees,
references,
new CSharpCompilationOptions(outputKind)
- .WithOptimizationLevel(OptimizationLevel.Release)
+ .WithOptimizationLevel(OptimizationLevel.Debug)
.WithAllowUnsafe(true)
.WithNullableContextOptions(NullableContextOptions.Enable));
@@ -97,7 +85,7 @@ public string CompileAssembly(
.Where(d => d.Severity == DiagnosticSeverity.Error)
.Select(d => d.ToString());
throw new InvalidOperationException(
- $"Compilation of '{assemblyName}' failed:\n{string.Join("\n", errors)}");
+ $"Compilation of '{assemblyName}' failed:\nReferences:\n{string.Join(Environment.NewLine, compilation.References.Select(r => r.Display))}\n\n{string.Join("\n", errors)}");
}
return outputPath;
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestRunner.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestRunner.cs
index c6a2e26649671e..dc23a142c40946 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestRunner.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestRunner.cs
@@ -11,6 +11,12 @@
namespace ILCompiler.ReadyToRun.Tests.TestCasesRunner;
+enum TargetRid
+{
+ Host,
+ BrowserWasm
+}
+
///
/// Describes an assembly compiled by Roslyn as part of a test case.
///
@@ -94,6 +100,16 @@ internal sealed class CrossgenCompilation(string name, List as
///
public Action? Validate { get; init; }
+ ///
+ /// When true, this compilation references the browser-wasm runtime pack (real wasm framework
+ /// assemblies and the wasm System.Private.CoreLib) if it is available, instead of the host
+ /// runtime pack. When the browser-wasm libs are not present the runner falls back to host
+ /// references so the test still runs (with a host-arch corelib). Set this on wasm compilations
+ /// (--targetarch wasm --targetos browser) so they reference target-matching libraries when run
+ /// in a job that downloads the browser-wasm libs.
+ ///
+ public TargetRid TargetRid { get; init; } = TargetRid.Host;
+
public string Name => name;
public bool IsComposite => Options.Contains(Crossgen2Option.Composite);
@@ -205,10 +221,18 @@ public void Run(R2RTestCase testCase)
// Step 2: Run each crossgen2 compilation and validate
var driver = new R2RDriver(_output, _paths);
- var refPaths = BuildReferencePaths();
+ List hostRefPaths = BuildReferencePaths(useWasmReferences: false);
+ List? wasmRefPaths = null;
foreach(var compilation in testCase.Compilations)
{
+ List refPaths = hostRefPaths;
+ if (compilation.TargetRid == TargetRid.BrowserWasm)
+ {
+ wasmRefPaths ??= BuildReferencePaths(useWasmReferences: true);
+ refPaths = wasmRefPaths;
+ }
+
string outputPath = RunCrossgenCompilation(
testCase.Name, compilation, driver, compilation.FilePath, refPaths, assemblyPaths);
@@ -216,7 +240,7 @@ public void Run(R2RTestCase testCase)
{
Assert.True(File.Exists(outputPath), $"R2R image not found: {outputPath}");
_output.WriteLine($" Validating R2R image: {outputPath}");
- var reader = new ReadyToRunReader(new SimpleAssemblyResolver(_paths), outputPath);
+ var reader = new ReadyToRunReader(new SimpleAssemblyResolver(refPaths), outputPath);
compilation.Validate(reader);
}
}
@@ -234,11 +258,13 @@ public void Run(R2RTestCase testCase)
private Dictionary CompileAllAssemblies(
IEnumerable assemblies)
{
- var compiler = new R2RTestCaseCompiler(_paths);
var paths = new Dictionary();
foreach (var asm in assemblies)
{
+ // Tests shouldn't require a platform-specific runtime/ref pack for Roslyn compilation
+ var defaultReferences = BuildReferencePaths(useWasmReferences: false);
+ var compiler = new R2RTestCaseCompiler(defaultReferences);
var sources = asm.SourceResourceNames
.Select(R2RTestCaseCompiler.ReadEmbeddedSource)
.ToList();
@@ -252,7 +278,7 @@ private Dictionary CompileAllAssemblies(
additionalReferences: asm.References.Select(r => r.FilePath).ToList(),
features: asm.Features.Count > 0 ? asm.Features : null);
paths[asm.AssemblyName] = ilPath;
- _output.WriteLine($" Roslyn compiled '{asm.AssemblyName}' -> {ilPath}");
+ _output.WriteLine($"Roslyn compiled '{asm.AssemblyName}' -> {ilPath}");
}
return paths;
@@ -307,6 +333,13 @@ private static string RunCrossgenCompilation(
foreach (var option in compilation.Options)
args.Add(option.ToArg());
+ args.AddRange(compilation.TargetRid switch
+ {
+ TargetRid.Host => [],
+ TargetRid.BrowserWasm => ["--targetos", "browser", "--targetarch", "wasm"],
+ _ => throw new InvalidOperationException($"Unknown target RID: {compilation.TargetRid}")
+ });
+
// Caller-supplied raw args (for options that take values, e.g. --determinism-stress=N)
args.AddRange(compilation.AdditionalArgs);
@@ -335,32 +368,47 @@ private static void AddRefArgs(List args, List refPaths)
}
}
- private List BuildReferencePaths()
+ ///
+ /// Runtime packs are used as references for both Roslyn compilation and crossgen2 compilation. This method builds the reference paths for both.
+ ///
+ /// Whether to try to use browser-wasm references.
+ private List BuildReferencePaths(bool useWasmReferences)
{
- var paths = new List();
+ List paths;
- paths.Add(Path.Combine(_paths.RuntimePackDir, "*.dll"));
-
- // SPCL lives in the runtime pack native/ dir in full builds (placed by
- // externals.csproj BinPlace during libs.pretest). In partial CI builds
- // that skip libs.pretest, the runtime pack layout may not exist, but the
- // CoreCLR artifacts directory always has SPCL after clr.nativecorelib.
- string spcl = Path.Combine(_paths.RuntimePackNativeDir, "System.Private.CoreLib.dll");
- if (!File.Exists(spcl))
+ if (useWasmReferences)
{
- string fallback = Path.Combine(_paths.CoreCLRArtifactsDir, "System.Private.CoreLib.dll");
- if (File.Exists(fallback))
+ string? wasmRuntimePackDir = _paths.WasmRuntimePackDir;
+ string? wasmCoreLibPath = _paths.WasmRuntimePackNativeDir is null
+ ? null
+ : Path.Combine(_paths.WasmRuntimePackNativeDir, "System.Private.CoreLib.dll");
+
+ if (wasmRuntimePackDir is not null && wasmCoreLibPath is not null && File.Exists(wasmCoreLibPath))
{
- _output.WriteLine($"[R2RTestRunner] SPCL not found at '{spcl}'; using CoreCLR artifacts fallback '{fallback}'");
- spcl = fallback;
+ _output.WriteLine($"Using browser-wasm runtime pack references from '{wasmRuntimePackDir}'");
+ paths =
+ [
+ ..Directory.GetFiles(wasmRuntimePackDir, "*.dll"),
+ wasmCoreLibPath
+ ];
+ return paths;
+ }
+ else
+ {
+ Assert.False(
+ _paths.RequireWasmReferences,
+ "Browser-wasm framework references are required, but the browser-wasm runtime pack was not found.");
+ _output.WriteLine($"Browser-wasm runtime pack references not available. Falling back to host references.");
}
}
- Assert.True(File.Exists(spcl),
- $"System.Private.CoreLib.dll not found at '{spcl}'. " +
- $"Searched RuntimePackNativeDir='{_paths.RuntimePackNativeDir}' and " +
- $"CoreCLRArtifactsDir='{_paths.CoreCLRArtifactsDir}'");
- paths.Add(spcl);
+ string coreLibPath = Path.Combine(_paths.RuntimePackNativeDir, "System.Private.CoreLib.dll");
+ Assert.True(File.Exists(coreLibPath), $"System.Private.CoreLib.dll not found: {coreLibPath}");
+
+ paths = [
+ ..Directory.GetFiles(_paths.RuntimePackDir, "*.dll"),
+ coreLibPath
+ ];
return paths;
}
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/TestPaths.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/TestPaths.cs
index be18b5ff9aeb91..eec3b189895884 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/TestPaths.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/TestPaths.cs
@@ -2,7 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System;
-using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
@@ -29,51 +28,10 @@ private static string GetRequiredConfig(string key)
?? throw new InvalidOperationException($"Missing RuntimeHostConfigurationOption '{key}'. Was the project built with the correct properties?");
}
- ///
- /// Tries to find an existing directory by swapping the build configuration path segment
- /// (Debug, Release, Checked) in the path. Returns the first match, or the original
- /// path if no fallback exists.
- ///
- private string ProbeConfigFallback(string dir)
+ private static string? GetOptionalConfig(string key)
{
- if (Directory.Exists(dir))
- return dir;
-
- foreach (string fallbackConfig in new[] { "Release", "Checked", "Debug" })
- {
- string fallback = Regex.Replace(
- dir, @"(?<=[/\\])(Debug|Release|Checked)(?=[/\\])", fallbackConfig);
- if (fallback != dir && Directory.Exists(fallback))
- {
- _output.WriteLine($"[TestPaths] '{dir}' not found; falling back to '{fallback}'");
- return fallback;
- }
- }
-
- return dir;
- }
-
- ///
- /// Like but for paths where the configuration name
- /// is embedded in a dot-delimited segment (e.g. linux.x64.Checked).
- ///
- private string ProbeDottedConfigFallback(string dir)
- {
- if (Directory.Exists(dir))
- return dir;
-
- foreach (string fallbackConfig in new[] { "Checked", "Release", "Debug" })
- {
- string fallback = Regex.Replace(
- dir, @"\.(Debug|Release|Checked)([/\\])", $".{fallbackConfig}$2");
- if (fallback != dir && Directory.Exists(fallback))
- {
- _output.WriteLine($"[TestPaths] '{dir}' not found; falling back to '{fallback}'");
- return fallback;
- }
- }
-
- return dir;
+ string? value = AppContext.GetData(key) as string;
+ return string.IsNullOrEmpty(value) ? null : value;
}
///
@@ -81,24 +39,9 @@ private string ProbeDottedConfigFallback(string dir)
/// e.g. artifacts/bin/coreclr/linux.x64.Checked/x64/crossgen2/
/// Falls back to Checked or Release if the configured path doesn't exist.
///
- public string Crossgen2Dir
- {
- get
- {
- string dir = GetRequiredConfig("R2RTest.Crossgen2Dir");
- if (!File.Exists(Path.Combine(dir, Crossgen2ExeName)))
- {
- string fallback = ProbeDottedConfigFallback(dir);
- if (File.Exists(Path.Combine(fallback, Crossgen2ExeName)))
- return fallback;
- }
+ public string Crossgen2Dir => GetRequiredConfig("R2RTest.Crossgen2Dir");
- return dir;
- }
- }
-
- private static string Crossgen2ExeName =>
- RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "crossgen2.exe" : "crossgen2";
+ private static string Crossgen2ExeName => RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "crossgen2.exe" : "crossgen2";
///
/// Path to the self-contained crossgen2 executable (from crossgen2_inbuild).
@@ -106,97 +49,37 @@ public string Crossgen2Dir
public string Crossgen2Exe => Path.Combine(Crossgen2Dir, Crossgen2ExeName);
///
- /// Path to the runtime pack managed assemblies directory.
- /// e.g. artifacts/bin/microsoft.netcore.app.runtime.linux-x64/Release/runtimes/linux-x64/lib/net11.0/
- /// Falls back to a different build configuration if the path doesn't exist.
- ///
- public string RuntimePackDir
- {
- get
- {
- string dir = GetRequiredConfig("R2RTest.RuntimePackDir");
- return ProbeConfigFallback(dir);
- }
- }
-
- ///
- /// Path to the runtime pack native directory (contains System.Private.CoreLib.dll and native runtime).
- /// e.g. artifacts/bin/microsoft.netcore.app.runtime.linux-x64/Release/runtimes/linux-x64/native/
- /// Falls back to a different build configuration if the path doesn't exist.
+ /// Path to the host runtime pack managed assemblies directory.
///
- public string RuntimePackNativeDir
- {
- get
- {
- string dir = GetRequiredConfig("R2RTest.RuntimePackNativeDir");
- return ProbeConfigFallback(dir);
- }
- }
+ public string RuntimePackDir => GetRequiredConfig("R2RTest.RuntimePackDir");
///
- /// Path to the CoreCLR artifacts directory (contains native bits like corerun).
- /// e.g. artifacts/bin/coreclr/linux.x64.Checked/
- /// Falls back to Checked or Release if the configured path doesn't exist.
+ /// Path to the host runtime pack native directory.
///
- public string CoreCLRArtifactsDir
- {
- get
- {
- string dir = GetRequiredConfig("R2RTest.CoreCLRArtifactsDir");
- return ProbeDottedConfigFallback(dir);
- }
- }
-
- public static string TargetArchitecture => GetRequiredConfig("R2RTest.TargetArchitecture");
- public static string TargetOS => GetRequiredConfig("R2RTest.TargetOS");
- public static string CoreCLRConfiguration => GetRequiredConfig("R2RTest.CoreCLRConfiguration");
- public static bool IsReleaseCoreCLR => string.Equals(CoreCLRConfiguration, "Release", StringComparison.OrdinalIgnoreCase);
- public static bool IsNotReleaseCoreCLR => !IsReleaseCoreCLR;
+ public string RuntimePackNativeDir => GetRequiredConfig("R2RTest.RuntimePackNativeDir");
///
- /// Path to the reference assembly pack (for Roslyn compilation).
- /// e.g. artifacts/bin/microsoft.netcore.app.ref/ref/net11.0/
+ /// Path to the browser-wasm runtime pack managed assemblies directory, or null when the
+ /// browser-wasm runtime pack has not been built/downloaded (e.g. in the default linux-x64 tools
+ /// test job). When present, the wasm crossgen2 compilation references these real browser-wasm
+ /// framework assemblies instead of the host runtime pack.
+ /// e.g. artifacts/bin/microsoft.netcore.app.runtime.browser-wasm/Release/runtimes/browser-wasm/lib/net11.0/
///
- public string RefPackDir
- {
- get
- {
- string dir = GetRequiredConfig("R2RTest.RefPackDir");
- if (!Directory.Exists(dir))
- {
- // Try the artifacts/bin/ref/net* fallback
- string artifactsBin = Path.GetFullPath(Path.Combine(CoreCLRArtifactsDir, "..", ".."));
- string refDir = Path.Combine(artifactsBin, "ref");
- if (Directory.Exists(refDir))
- {
- foreach (string subDir in Directory.GetDirectories(refDir, "net*"))
- {
- if (File.Exists(Path.Combine(subDir, "System.Runtime.dll")))
- {
- _output.WriteLine($"[TestPaths] '{dir}' not found; falling back to '{subDir}'");
- return subDir;
- }
- }
- }
- }
-
- return dir;
- }
- }
+ public string? WasmRuntimePackDir => GetOptionalConfig("R2RTest.WasmRuntimePackDir");
///
- /// Returns the target triple string for crossgen2 (e.g. "linux-x64").
+ /// Path to the browser-wasm runtime pack native directory (contains the wasm
+ /// System.Private.CoreLib.dll), or null when not available.
+ /// e.g. artifacts/bin/microsoft.netcore.app.runtime.browser-wasm/Release/runtimes/browser-wasm/native/
///
- public static string TargetTriple => $"{TargetOS.ToLowerInvariant()}-{TargetArchitecture.ToLowerInvariant()}";
+ public string? WasmRuntimePackNativeDir => GetOptionalConfig("R2RTest.WasmRuntimePackNativeDir");
- ///
- /// Returns all framework reference assembly paths (*.dll in the runtime pack).
- ///
- public IEnumerable GetFrameworkReferencePaths()
- {
- if (!Directory.Exists(RuntimePackDir))
- throw new DirectoryNotFoundException($"Runtime pack directory not found: {RuntimePackDir}");
+ public bool RequireWasmReferences => string.Equals(
+ GetOptionalConfig("R2RTest.RequireWasmReferences"),
+ bool.TrueString,
+ StringComparison.OrdinalIgnoreCase);
- return Directory.EnumerateFiles(RuntimePackDir, "*.dll");
- }
+ public static string CoreCLRConfiguration => GetRequiredConfig("R2RTest.CoreCLRConfiguration");
+ public static bool IsReleaseCoreCLR => string.Equals(CoreCLRConfiguration, "Release", StringComparison.OrdinalIgnoreCase);
+ public static bool IsNotReleaseCoreCLR => !IsReleaseCoreCLR;
}
diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.CoreCLR.sfxproj b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.CoreCLR.sfxproj
index 45a5d877f608a9..61dcef292c508d 100644
--- a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.CoreCLR.sfxproj
+++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.CoreCLR.sfxproj
@@ -15,7 +15,7 @@
true
true
- true
+ true
From 4f17299b5e9f48777b19a709e259fcdb663a3175 Mon Sep 17 00:00:00 2001
From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com>
Date: Sat, 18 Jul 2026 18:11:37 -0700
Subject: [PATCH 02/17] Clean up ILCompiler.ReadyToRun.Tests.csproj
- Use CopyAllJitLibrariesToAotCompilerOutput=true instead of copying ourselves.
- Set crossgenDir based on crossgen2_inbuild.csproj output items
- Reorder properties and items to keep related items close to each other
---
.../ILCompiler.ReadyToRun.Tests.csproj | 139 +++++++-----------
1 file changed, 55 insertions(+), 84 deletions(-)
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj
index 14cec02ab0230e..9c2ff4fdf2e558 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj
@@ -13,11 +13,6 @@
true
-notrait category=failing
false
- $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'microsoft.netcore.app.runtime.browser-wasm'))
- $([MSBuild]::NormalizeDirectory('$(WasmRuntimePackRoot)', '$(LibrariesConfiguration)', 'runtimes', 'browser-wasm', 'lib', '$(NetCoreAppCurrent)'))
- $([MSBuild]::NormalizeDirectory('$(WasmRuntimePackRoot)', '$(LibrariesConfiguration)', 'runtimes', 'browser-wasm', 'native'))
- $([MSBuild]::NormalizeDirectory('$(WasmRuntimePackRoot)', 'Release', 'runtimes', 'browser-wasm', 'lib', '$(NetCoreAppCurrent)'))
- $([MSBuild]::NormalizeDirectory('$(WasmRuntimePackRoot)', 'Release', 'runtimes', 'browser-wasm', 'native'))
@@ -25,57 +20,33 @@
+
+
+
+
+
+
-
-
- CopyAllJitLibrariesToAotCompilerOutput=true
- Configuration=$(CoreCLRConfiguration);CopyAllJitLibrariesToAotCompilerOutput=true
+
+ Configuration=$(CoreCLRConfiguration);CopyAllJitLibrariesToAotCompilerOutput=true
-
-
- <_BuildJitsCrossArch Condition="'$(CrossBuild)' == 'true' or '$(BuildArchitecture)' != '$(TargetArchitecture)' or '$(HostOS)' != '$(TargetOS)' or '$(EnableNativeSanitizers)' != ''">true
- <_BuildJitsProperties>ClrAllJitsSubset=true;ClrWasmJitSubset=true;Configuration=$(CoreCLRConfiguration)
- <_BuildJitsProperties Condition="!$([MSBuild]::IsOsPlatform(Windows))">$(_BuildJitsProperties);Ninja=true
- <_BuildJitsProperties Condition="'$(_BuildJitsCrossArch)' == 'true'">$(_BuildJitsProperties);HostArchitecture=$(BuildArchitecture);HostCrossOS=$(HostOS);CrossBuild=false;BuildSubdirectory=$(BuildArchitecture);CMakeArgs=$(CMakeArgs) -DCLR_CROSS_COMPONENTS_BUILD=1;PgoInstrument=false;NoPgoOptimize=true
-
-
+
+
+ <_BuildJitsCrossArch Condition="'$(CrossBuild)' == 'true' or '$(BuildArchitecture)' != '$(TargetArchitecture)' or '$(HostOS)' != '$(TargetOS)' or '$(EnableNativeSanitizers)' != ''">true
+ <_BuildJitsProperties>ClrAllJitsSubset=true;ClrWasmJitSubset=true;Configuration=$(CoreCLRConfiguration)
+ <_BuildJitsProperties Condition="!$([MSBuild]::IsOsPlatform(Windows))">$(_BuildJitsProperties);Ninja=true
+ <_BuildJitsProperties Condition="'$(_BuildJitsCrossArch)' == 'true'">$(_BuildJitsProperties);HostArchitecture=$(BuildArchitecture);HostCrossOS=$(HostOS);CrossBuild=false;BuildSubdirectory=$(BuildArchitecture);CMakeArgs=$(CMakeArgs) -DCLR_CROSS_COMPONENTS_BUILD=1;PgoInstrument=false;NoPgoOptimize=true
+
-
-
- <_WasmJitCrossHostArchSubdir Condition="'$(_BuildJitsCrossArch)' == 'true'">$(BuildArchitecture)/
- <_WasmJitFileName>$(HostLibPrefix)clrjit_universal_wasm_$(BuildArchitecture)$(HostLibSuffix)
- <_WasmJitSourcePath>$(CoreCLRArtifactsPath)$(_WasmJitCrossHostArchSubdir)$(_WasmJitFileName)
- <_Crossgen2InbuildPublishDir>$(CoreCLRArtifactsPath)$(BuildArchitecture)/crossgen2/
-
-
+
-
-
-
-
-
+
+
-
-
-
-
- $(CoreCLRArtifactsPath)/$(BuildArchitecture)/crossgen2
-
-
- $(MicrosoftNetCoreAppRuntimePackRidLibTfmDir)
-
-
- $(MicrosoftNetCoreAppRuntimePackNativeDir)
-
+
+ $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'microsoft.netcore.app.runtime.browser-wasm'))
+ $([MSBuild]::NormalizeDirectory('$(WasmRuntimePackRoot)', '$(LibrariesConfiguration)', 'runtimes', 'browser-wasm', 'lib', '$(NetCoreAppCurrent)'))
+ $([MSBuild]::NormalizeDirectory('$(WasmRuntimePackRoot)', '$(LibrariesConfiguration)', 'runtimes', 'browser-wasm', 'native'))
+ $([MSBuild]::NormalizeDirectory('$(WasmRuntimePackRoot)', 'Release', 'runtimes', 'browser-wasm', 'lib', '$(NetCoreAppCurrent)'))
+ $([MSBuild]::NormalizeDirectory('$(WasmRuntimePackRoot)', 'Release', 'runtimes', 'browser-wasm', 'native'))
+
-
-
- $(RequireWasmReferences)
-
-
- $(WasmRuntimePackDir)
-
-
- $(WasmRuntimePackNativeDir)
-
+
+
+ @(Crossgen2InbuildOutputItems->WithMetadataValue('Filename', 'crossgen2')->WithMetadataValue('Extension', '$(ExeSuffix)')->'%(RootDir)%(Directory)')
+
+
+ $(MicrosoftNetCoreAppRuntimePackRidLibTfmDir)
+
+
+ $(MicrosoftNetCoreAppRuntimePackNativeDir)
+
-
-
- $(CoreCLRConfiguration)
-
-
+
+ $(RequireWasmReferences)
+
+
+ $(WasmRuntimePackDir)
+
+
+ $(WasmRuntimePackNativeDir)
+
+
+
+
+ $(CoreCLRConfiguration)
+
+
+
+
From 898d14be4ef04bcfb6b9b49966d39551e763451f Mon Sep 17 00:00:00 2001
From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com>
Date: Sat, 18 Jul 2026 18:32:00 -0700
Subject: [PATCH 03/17] Cleanup
---
.../TestCases/R2RTestSuites.cs | 13 +++++++++----
.../TestCasesRunner/R2RDriver.cs | 2 --
.../TestCasesRunner/R2RTestCaseCompiler.cs | 3 +--
.../TestCasesRunner/R2RTestRunner.cs | 10 ++--------
.../TestCasesRunner/TestPaths.cs | 1 +
5 files changed, 13 insertions(+), 16 deletions(-)
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs
index 8750b3551ff007..4c92350b7fe581 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs
@@ -150,7 +150,12 @@ static void Validate(ReadyToRunReader reader)
}
}
- [Fact]
+ // Note: The arm tests compile for an arm32 target, but they are compiled with the host Corelib. If there are
+ // load-bearing dependencies in the arm32 corelib (that aren't present in the host corelib) that crossgen2 requires,
+ // these tests may fail. Wasm tests are similar, but since it doesn't require a special cross-build setup, they use
+ // a wasm corelib in CI. arm32 tests could be similarly set up, but it hasn't been done yet.
+
+ [ConditionalFact(typeof(TestPaths), nameof(TestPaths.ArmOnHostOSSupported))]
public void ArmThumbBitRelocationTargets()
{
var inlineableLib = new CompiledAssembly
@@ -174,7 +179,7 @@ public void ArmThumbBitRelocationTargets()
new CrossgenAssembly(inlineableLib) { Kind = Crossgen2InputKind.Reference },
])
{
- Options = [Crossgen2Option.TargetArchArm],
+ TargetRid = TargetRid.HostArm,
Validate = Validate,
},
]));
@@ -187,7 +192,7 @@ static void Validate(ReadyToRunReader reader)
}
// JitStressProcedureSplitting is only available in Debug/Checked JIT builds.
- [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsNotReleaseCoreCLR))]
+ [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsNotReleaseCoreCLR), nameof(TestPaths.ArmOnHostOSSupported))]
public void ArmThumbBitHotColdRuntimeFunctions()
{
var hotColdSplitting = new CompiledAssembly
@@ -201,9 +206,9 @@ public void ArmThumbBitHotColdRuntimeFunctions()
[
new(nameof(ArmThumbBitHotColdRuntimeFunctions), [new CrossgenAssembly(hotColdSplitting)])
{
+ TargetRid = TargetRid.HostArm,
Options =
[
- Crossgen2Option.TargetArchArm,
Crossgen2Option.Optimize,
Crossgen2Option.HotColdSplitting,
],
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RDriver.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RDriver.cs
index a359f7bfa45d05..c598f917dc0787 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RDriver.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RDriver.cs
@@ -32,7 +32,6 @@ internal enum Crossgen2Option
InputBubble,
HotColdSplitting,
Optimize,
- TargetArchArm,
StripILBodies,
}
@@ -59,7 +58,6 @@ internal static class Crossgen2OptionsExtensions
Crossgen2Option.InputBubble => $"--input-bubble",
Crossgen2Option.HotColdSplitting => $"--hot-cold-splitting",
Crossgen2Option.Optimize => $"--optimize",
- Crossgen2Option.TargetArchArm => $"--targetarch:arm",
Crossgen2Option.StripILBodies => $"--strip-il-bodies",
_ => throw new ArgumentOutOfRangeException(nameof(kind)),
};
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestCaseCompiler.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestCaseCompiler.cs
index cffccdf5c65b34..816801c96cdc6c 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestCaseCompiler.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestCaseCompiler.cs
@@ -9,7 +9,6 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Emit;
-using Xunit.Abstractions;
namespace ILCompiler.ReadyToRun.Tests.TestCasesRunner;
@@ -85,7 +84,7 @@ public string CompileAssembly(
.Where(d => d.Severity == DiagnosticSeverity.Error)
.Select(d => d.ToString());
throw new InvalidOperationException(
- $"Compilation of '{assemblyName}' failed:\nReferences:\n{string.Join(Environment.NewLine, compilation.References.Select(r => r.Display))}\n\n{string.Join("\n", errors)}");
+ $"Compilation of '{assemblyName}' failed:\n{string.Join("\n", errors)}");
}
return outputPath;
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestRunner.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestRunner.cs
index dc23a142c40946..769cdad89c2773 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestRunner.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestRunner.cs
@@ -14,6 +14,7 @@ namespace ILCompiler.ReadyToRun.Tests.TestCasesRunner;
enum TargetRid
{
Host,
+ HostArm,
BrowserWasm
}
@@ -100,14 +101,6 @@ internal sealed class CrossgenCompilation(string name, List as
///
public Action? Validate { get; init; }
- ///
- /// When true, this compilation references the browser-wasm runtime pack (real wasm framework
- /// assemblies and the wasm System.Private.CoreLib) if it is available, instead of the host
- /// runtime pack. When the browser-wasm libs are not present the runner falls back to host
- /// references so the test still runs (with a host-arch corelib). Set this on wasm compilations
- /// (--targetarch wasm --targetos browser) so they reference target-matching libraries when run
- /// in a job that downloads the browser-wasm libs.
- ///
public TargetRid TargetRid { get; init; } = TargetRid.Host;
public string Name => name;
@@ -336,6 +329,7 @@ private static string RunCrossgenCompilation(
args.AddRange(compilation.TargetRid switch
{
TargetRid.Host => [],
+ TargetRid.HostArm => ["--targetarch", "arm"],
TargetRid.BrowserWasm => ["--targetos", "browser", "--targetarch", "wasm"],
_ => throw new InvalidOperationException($"Unknown target RID: {compilation.TargetRid}")
});
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/TestPaths.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/TestPaths.cs
index eec3b189895884..4947a4604dd50a 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/TestPaths.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/TestPaths.cs
@@ -82,4 +82,5 @@ private static string GetRequiredConfig(string key)
public static string CoreCLRConfiguration => GetRequiredConfig("R2RTest.CoreCLRConfiguration");
public static bool IsReleaseCoreCLR => string.Equals(CoreCLRConfiguration, "Release", StringComparison.OrdinalIgnoreCase);
public static bool IsNotReleaseCoreCLR => !IsReleaseCoreCLR;
+ public static bool ArmOnHostOSSupported => RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
}
From 08fc40a5d8fcf5bda8f392f8ea00e1f0c25b7295 Mon Sep 17 00:00:00 2001
From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com>
Date: Mon, 20 Jul 2026 09:07:50 -0700
Subject: [PATCH 04/17] Address copilot feedback
---
.../ILCompiler.ReadyToRun.Tests.csproj | 18 ++++++++++--------
.../TestCasesRunner/R2RTestRunner.cs | 6 +++---
.../TestCasesRunner/TestPaths.cs | 1 -
3 files changed, 13 insertions(+), 12 deletions(-)
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj
index 9c2ff4fdf2e558..7d99e13fac1471 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj
@@ -34,8 +34,18 @@
+
+ $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'microsoft.netcore.app.runtime.browser-wasm'))
+ $([MSBuild]::NormalizeDirectory('$(WasmRuntimePackRoot)', '$(LibrariesConfiguration)', 'runtimes', 'browser-wasm', 'lib', '$(NetCoreAppCurrent)'))
+ $([MSBuild]::NormalizeDirectory('$(WasmRuntimePackRoot)', '$(LibrariesConfiguration)', 'runtimes', 'browser-wasm', 'native'))
+ $([MSBuild]::NormalizeDirectory('$(WasmRuntimePackRoot)', 'Release', 'runtimes', 'browser-wasm', 'lib', '$(NetCoreAppCurrent)'))
+ $([MSBuild]::NormalizeDirectory('$(WasmRuntimePackRoot)', 'Release', 'runtimes', 'browser-wasm', 'native'))
+
+
+
@@ -74,14 +84,6 @@
BeforeTargets="GenerateBuildRuntimeConfigurationFiles"
Condition="'$(DesignTimeBuild)' != 'true'">
-
- $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'microsoft.netcore.app.runtime.browser-wasm'))
- $([MSBuild]::NormalizeDirectory('$(WasmRuntimePackRoot)', '$(LibrariesConfiguration)', 'runtimes', 'browser-wasm', 'lib', '$(NetCoreAppCurrent)'))
- $([MSBuild]::NormalizeDirectory('$(WasmRuntimePackRoot)', '$(LibrariesConfiguration)', 'runtimes', 'browser-wasm', 'native'))
- $([MSBuild]::NormalizeDirectory('$(WasmRuntimePackRoot)', 'Release', 'runtimes', 'browser-wasm', 'lib', '$(NetCoreAppCurrent)'))
- $([MSBuild]::NormalizeDirectory('$(WasmRuntimePackRoot)', 'Release', 'runtimes', 'browser-wasm', 'native'))
-
-
@(Crossgen2InbuildOutputItems->WithMetadataValue('Filename', 'crossgen2')->WithMetadataValue('Extension', '$(ExeSuffix)')->'%(RootDir)%(Directory)')
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestRunner.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestRunner.cs
index 769cdad89c2773..850bf53ffc0750 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestRunner.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestRunner.cs
@@ -253,11 +253,11 @@ private Dictionary CompileAllAssemblies(
{
var paths = new Dictionary();
+ // Tests shouldn't require a platform-specific runtime/ref pack for Roslyn compilation
+ var defaultReferences = BuildReferencePaths(useWasmReferences: false);
+ var compiler = new R2RTestCaseCompiler(defaultReferences);
foreach (var asm in assemblies)
{
- // Tests shouldn't require a platform-specific runtime/ref pack for Roslyn compilation
- var defaultReferences = BuildReferencePaths(useWasmReferences: false);
- var compiler = new R2RTestCaseCompiler(defaultReferences);
var sources = asm.SourceResourceNames
.Select(R2RTestCaseCompiler.ReadEmbeddedSource)
.ToList();
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/TestPaths.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/TestPaths.cs
index 4947a4604dd50a..71661567fe2daf 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/TestPaths.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/TestPaths.cs
@@ -37,7 +37,6 @@ private static string GetRequiredConfig(string key)
///
/// Path to the crossgen2 output directory (contains the self-contained crossgen2 executable and clrjit).
/// e.g. artifacts/bin/coreclr/linux.x64.Checked/x64/crossgen2/
- /// Falls back to Checked or Release if the configured path doesn't exist.
///
public string Crossgen2Dir => GetRequiredConfig("R2RTest.Crossgen2Dir");
From 7e40733b9c8cd0c7f00e32825e438411df6cda41 Mon Sep 17 00:00:00 2001
From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com>
Date: Mon, 20 Jul 2026 14:05:33 -0700
Subject: [PATCH 05/17] Check runtime pack dir exists before enumerating
---
.../TestCasesRunner/R2RTestRunner.cs | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestRunner.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestRunner.cs
index 850bf53ffc0750..e8c1704bd3fd58 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestRunner.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestRunner.cs
@@ -365,7 +365,7 @@ private static void AddRefArgs(List args, List refPaths)
///
/// Runtime packs are used as references for both Roslyn compilation and crossgen2 compilation. This method builds the reference paths for both.
///
- /// Whether to try to use browser-wasm references.
+ /// Whether to try to use browser-wasm references. The host references are used as a fallback if browser-wasm references are not available.
private List BuildReferencePaths(bool useWasmReferences)
{
List paths;
@@ -377,7 +377,8 @@ private List BuildReferencePaths(bool useWasmReferences)
? null
: Path.Combine(_paths.WasmRuntimePackNativeDir, "System.Private.CoreLib.dll");
- if (wasmRuntimePackDir is not null && wasmCoreLibPath is not null && File.Exists(wasmCoreLibPath))
+ if (wasmRuntimePackDir is not null && wasmCoreLibPath is not null
+ && File.Exists(wasmCoreLibPath) && Directory.Exists(wasmRuntimePackDir))
{
_output.WriteLine($"Using browser-wasm runtime pack references from '{wasmRuntimePackDir}'");
paths =
@@ -398,6 +399,7 @@ private List BuildReferencePaths(bool useWasmReferences)
string coreLibPath = Path.Combine(_paths.RuntimePackNativeDir, "System.Private.CoreLib.dll");
Assert.True(File.Exists(coreLibPath), $"System.Private.CoreLib.dll not found: {coreLibPath}");
+ Assert.True(Directory.Exists(_paths.RuntimePackDir), $"Runtime pack directory not found: {_paths.RuntimePackDir}");
paths = [
..Directory.GetFiles(_paths.RuntimePackDir, "*.dll"),
From fd66c73edb747a082bef19948e8466a53aada1a0 Mon Sep 17 00:00:00 2001
From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com>
Date: Thu, 23 Jul 2026 10:59:00 -0700
Subject: [PATCH 06/17] Download built arm64 and wasm runtime packs for new
clr.r2rtests subset
---
eng/Subsets.props | 10 ++-
eng/pipelines/runtime.yml | 69 ++++++++++---------
.../ILCompiler.ReadyToRun.Tests.csproj | 56 +++++++--------
.../TestCases/R2RTestSuites.cs | 5 --
.../TestCasesRunner/R2RDriver.cs | 7 ++
.../TestCasesRunner/R2RTestCaseCompiler.cs | 2 +-
.../TestCasesRunner/R2RTestRunner.cs | 58 ++++++++--------
.../TestCasesRunner/TestPaths.cs | 26 +++----
...rosoft.NETCore.App.Runtime.CoreCLR.sfxproj | 2 -
9 files changed, 117 insertions(+), 118 deletions(-)
diff --git a/eng/Subsets.props b/eng/Subsets.props
index 14b2d3eedab02a..549b541d80a2c0 100644
--- a/eng/Subsets.props
+++ b/eng/Subsets.props
@@ -163,7 +163,7 @@
clr.nativeprereqs+clr.iltools+clr.runtime+clr.native+clr.aot+clr.nativeaotlibs+clr.nativeaotruntime+clr.crossarchtools
- $(AllSubsetsExpansion)+clr.paltests+clr.paltestlist+clr.hosts+clr.jit+clr.alljits+clr.alljitscommunity+clr.spmi+clr.corelib+clr.nativecorelib+clr.tools+clr.toolstests+clr.packages
+ $(AllSubsetsExpansion)+clr.paltests+clr.paltestlist+clr.hosts+clr.jit+clr.alljits+clr.alljitscommunity+clr.spmi+clr.corelib+clr.nativecorelib+clr.tools+clr.toolstests+clr.r2rtests+clr.packages
$(AllSubsetsExpansion)+linuxdac+alpinedac
$(AllSubsetsExpansion)+mono.runtime+provision.emsdk+mono.aotcross+mono.corelib+mono.manifests+mono.packages+mono.tools+mono.wasmruntime+mono.wasiruntime+mono.wasmworkload+mono.mscordbi+mono.workloads
$(AllSubsetsExpansion)+tools.illink+tools.cdac+tools.illinktests+tools.cdactests+tools.cdacdumptests
@@ -227,6 +227,7 @@
+
@@ -508,14 +509,17 @@
Test="true" Category="clr" Condition="'$(DotNetBuildSourceOnly)' != 'true' and '$(NativeAotSupported)' == 'true'"/>
-
+
+
+
+
<_CDacToolsBuilt Condition="'$(_CDacToolsBuilt)' != 'true'">true
diff --git a/eng/pipelines/runtime.yml b/eng/pipelines/runtime.yml
index 156abcda32a04a..5dfa9e699e1aa9 100644
--- a/eng/pipelines/runtime.yml
+++ b/eng/pipelines/runtime.yml
@@ -223,6 +223,7 @@ extends:
eq(variables['wasmDarcDependenciesChanged'], true),
eq(stageDependencies.EvaluatePaths.evaluate_paths.outputs['SetPathVars_tools_illink.containsChange'], true),
eq(stageDependencies.EvaluatePaths.evaluate_paths.outputs['SetPathVars_wasm_coreclr_runtimetests.containsChange'], true),
+ eq(stageDependencies.EvaluatePaths.evaluate_paths.outputs['SetPathVars_coreclr.containsChange'], true),
eq(variables['isRollingBuild'], true))
- template: /eng/pipelines/common/platform-matrix.yml
@@ -872,24 +873,9 @@ extends:
jobParameters:
timeoutInMinutes: 120
nameSuffix: CLR_Tools_Tests
- # Depend on the browser-wasm libraries built below so the wasm crossgen2 R2R test
- # (ILCompiler.ReadyToRun.Tests) can compile against real browser-wasm references.
- dependsOn:
- - build_browser_wasm_linux_Release_CLR_Tools_Tests_WasmLibs
buildArgs: -s clr.aot+clr.iltools+clr.corelib+clr.nativecorelib+libs.sfx+clr.toolstests+tools.cdac+tools.cdactests -c $(_BuildConfig) -test
enablePublishTestResults: true
testResultsFormat: 'xunit'
- preBuildSteps:
- # Lay the browser-wasm libraries into artifacts/bin so TestPaths' WasmRuntimePackDir
- # resolves and the wasm R2R test compiles against real browser-wasm references.
- - template: /eng/pipelines/common/download-artifact-step.yml
- parameters:
- artifactName: CLR_Tools_Tests_WasmLibs_BuildArtifacts_browser_wasm_Release
- artifactFileName: CLR_Tools_Tests_WasmLibs_BuildArtifacts_browser_wasm_Release$(archiveExtension)
- unpackFolder: $(Build.SourcesDirectory)/artifacts/bin
- cleanUnpackFolder: false
- overwriteExistingFiles: true
- displayName: Browser-Wasm Libraries
# We want to run AOT tests when illink changes because there's share code and tests from illink which are used by AOT
condition: >-
or(
@@ -898,35 +884,52 @@ extends:
eq(stageDependencies.EvaluatePaths.evaluate_paths.outputs['SetPathVars_tools_cdac.containsChange'], true),
eq(variables['isRollingBuild'], true))
- # Build the browser-wasm managed runtime pack that the CLR_Tools_Tests job uses
- # as crossgen2 references for the wasm ReadyToRun test. Native runtime and host
- # assets are omitted because the test only consumes managed assemblies.
- template: /eng/pipelines/common/platform-matrix.yml
parameters:
jobTemplate: /eng/pipelines/common/global-build-job.yml
- buildConfig: Release
+ buildConfig: checked
platforms:
- - browser_wasm
+ - linux_x64
jobParameters:
- nameSuffix: CLR_Tools_Tests_WasmLibs
- buildArgs: -s clr.corelib+libs+libs.pretest -c Release /p:IncludeStaticLibrariesInPack=false
+ dependsOn:
+ - build_browser_wasm_linux_checked_CoreCLR_ReleaseLibraries
+ - build_linux_arm_checked_CoreCLR_ReleaseLibraries
+ - build_linux_x64_checked_CoreCLR_ReleaseLibraries
timeoutInMinutes: 120
- postBuildSteps:
- - template: /eng/pipelines/common/upload-artifact-step.yml
+ nameSuffix: CLR_R2R_Tests
+ buildArgs: -s clr.r2rtests -lc Release -c $(_BuildConfig) -test /p:RequireTargetArchRuntimePack=true
+ enablePublishTestResults: true
+ testResultsFormat: 'xunit'
+ preBuildSteps:
+ - template: /eng/pipelines/common/download-artifact-step.yml
parameters:
- rootFolder: $(Build.SourcesDirectory)/artifacts/bin
- includeRootFolder: false
- archiveType: $(archiveType)
- archiveExtension: $(archiveExtension)
- tarCompression: $(tarCompression)
- artifactName: CLR_Tools_Tests_WasmLibs_BuildArtifacts_$(osGroup)$(osSubgroup)_$(archType)_$(_BuildConfig)
- displayName: Browser-Wasm Libraries
+ artifactName: CoreCLR_ReleaseLibraries_BuildArtifacts_browser_wasm_linux_Checked
+ artifactFileName: CoreCLR_ReleaseLibraries_BuildArtifacts_browser_wasm_linux_Checked$(archiveExtension)
+ unpackFolder: $(Build.SourcesDirectory)/artifacts/bin
+ cleanUnpackFolder: false
+ overwriteExistingFiles: true
+ displayName: Download browser-wasm artifacts
+ - template: /eng/pipelines/common/download-artifact-step.yml
+ parameters:
+ artifactName: CoreCLR_ReleaseLibraries_BuildArtifacts_linux_arm_Checked
+ artifactFileName: CoreCLR_ReleaseLibraries_BuildArtifacts_linux_arm_Checked$(archiveExtension)
+ unpackFolder: $(Build.SourcesDirectory)/artifacts/bin
+ cleanUnpackFolder: false
+ overwriteExistingFiles: true
+ displayName: Download linux-arm artifacts
+ - template: /eng/pipelines/common/download-artifact-step.yml
+ parameters:
+ artifactName: CoreCLR_ReleaseLibraries_BuildArtifacts_linux_x64_Checked
+ artifactFileName: CoreCLR_ReleaseLibraries_BuildArtifacts_linux_x64_Checked$(archiveExtension)
+ unpackFolder: $(Build.SourcesDirectory)/artifacts/bin
+ cleanUnpackFolder: false
+ overwriteExistingFiles: true
+ displayName: Download linux-x64 artifacts
condition: >-
or(
eq(stageDependencies.EvaluatePaths.evaluate_paths.outputs['SetPathVars_coreclr.containsChange'], true),
- eq(stageDependencies.EvaluatePaths.evaluate_paths.outputs['SetPathVars_tools_illink.containsChange'], true),
- eq(stageDependencies.EvaluatePaths.evaluate_paths.outputs['SetPathVars_tools_cdac.containsChange'], true),
eq(variables['isRollingBuild'], true))
+
#
# Build CrossDacs
#
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj
index 7d99e13fac1471..17373e0c6f8457 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj
@@ -4,15 +4,10 @@
ILCompiler.ReadyToRun.Tests
$(NetCoreAppToolCurrent)
enable
- false
true
- x64;x86
- AnyCPU
- linux-x64;win-x64;osx-x64
- Debug;Release;Checked
true
-notrait category=failing
- false
+ false
@@ -40,24 +35,13 @@
$([MSBuild]::NormalizeDirectory('$(WasmRuntimePackRoot)', '$(LibrariesConfiguration)', 'runtimes', 'browser-wasm', 'native'))
$([MSBuild]::NormalizeDirectory('$(WasmRuntimePackRoot)', 'Release', 'runtimes', 'browser-wasm', 'lib', '$(NetCoreAppCurrent)'))
$([MSBuild]::NormalizeDirectory('$(WasmRuntimePackRoot)', 'Release', 'runtimes', 'browser-wasm', 'native'))
-
-
-
-
-
- <_BuildJitsCrossArch Condition="'$(CrossBuild)' == 'true' or '$(BuildArchitecture)' != '$(TargetArchitecture)' or '$(HostOS)' != '$(TargetOS)' or '$(EnableNativeSanitizers)' != ''">true
- <_BuildJitsProperties>ClrAllJitsSubset=true;ClrWasmJitSubset=true;Configuration=$(CoreCLRConfiguration)
- <_BuildJitsProperties Condition="!$([MSBuild]::IsOsPlatform(Windows))">$(_BuildJitsProperties);Ninja=true
- <_BuildJitsProperties Condition="'$(_BuildJitsCrossArch)' == 'true'">$(_BuildJitsProperties);HostArchitecture=$(BuildArchitecture);HostCrossOS=$(HostOS);CrossBuild=false;BuildSubdirectory=$(BuildArchitecture);CMakeArgs=$(CMakeArgs) -DCLR_CROSS_COMPONENTS_BUILD=1;PgoInstrument=false;NoPgoOptimize=true
-
-
-
+ $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'microsoft.netcore.app.runtime.$(TargetOS)-arm'))
+ $([MSBuild]::NormalizeDirectory('$(ArmRuntimePackRoot)', '$(LibrariesConfiguration)', 'runtimes', '$(TargetOS)-arm', 'lib', '$(NetCoreAppCurrent)'))
+ $([MSBuild]::NormalizeDirectory('$(ArmRuntimePackRoot)', '$(LibrariesConfiguration)', 'runtimes', '$(TargetOS)-arm', 'native'))
+ $([MSBuild]::NormalizeDirectory('$(ArmRuntimePackRoot)', 'Release', 'runtimes', '$(TargetOS)-arm', 'lib', '$(NetCoreAppCurrent)'))
+ $([MSBuild]::NormalizeDirectory('$(ArmRuntimePackRoot)', 'Release', 'runtimes', '$(TargetOS)-arm', 'native'))
+
-
-
-
+
+
-
+
+
@@ -95,9 +83,10 @@
$(MicrosoftNetCoreAppRuntimePackNativeDir)
-
- $(RequireWasmReferences)
+
+ $(RequireTargetArchRuntimePack)
+
$(WasmRuntimePackDir)
@@ -105,6 +94,13 @@
$(WasmRuntimePackNativeDir)
+
+ $(ArmRuntimePackDir)
+
+
+ $(ArmRuntimePackNativeDir)
+
+
$(CoreCLRConfiguration)
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs
index c0fc1616e29bc5..1223c2f30db233 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs
@@ -232,11 +232,6 @@ static void Validate(ReadyToRunReader reader)
}
}
- // Note: The arm tests compile for an arm32 target, but they are compiled with the host Corelib. If there are
- // load-bearing dependencies in the arm32 corelib (that aren't present in the host corelib) that crossgen2 requires,
- // these tests may fail. Wasm tests are similar, but since it doesn't require a special cross-build setup, they use
- // a wasm corelib in CI. arm32 tests could be similarly set up, but it hasn't been done yet.
-
[ConditionalFact(typeof(TestPaths), nameof(TestPaths.ArmOnHostOSSupported))]
public void ArmThumbBitRelocationTargets()
{
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RDriver.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RDriver.cs
index c598f917dc0787..24341440e31f35 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RDriver.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RDriver.cs
@@ -18,6 +18,13 @@ internal enum Crossgen2AssemblyOption
CrossModuleOptimization,
}
+internal enum CoreLibTargetArchitecture
+{
+ Host,
+ Arm32,
+ Wasm
+}
+
internal enum Crossgen2InputKind
{
InputAssembly,
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestCaseCompiler.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestCaseCompiler.cs
index 816801c96cdc6c..476ac89c09ce6e 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestCaseCompiler.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestCaseCompiler.cs
@@ -72,7 +72,7 @@ public string CompileAssembly(
syntaxTrees,
references,
new CSharpCompilationOptions(outputKind)
- .WithOptimizationLevel(OptimizationLevel.Debug)
+ .WithOptimizationLevel(OptimizationLevel.Release)
.WithAllowUnsafe(true)
.WithNullableContextOptions(NullableContextOptions.Enable));
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestRunner.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestRunner.cs
index e8c1704bd3fd58..ce88a5bff97241 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestRunner.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestRunner.cs
@@ -214,17 +214,23 @@ public void Run(R2RTestCase testCase)
// Step 2: Run each crossgen2 compilation and validate
var driver = new R2RDriver(_output, _paths);
- List hostRefPaths = BuildReferencePaths(useWasmReferences: false);
+ List hostRefPaths = BuildReferencePaths(CoreLibTargetArchitecture.Host);
List? wasmRefPaths = null;
+ List? armRefPaths = null;
foreach(var compilation in testCase.Compilations)
{
List refPaths = hostRefPaths;
if (compilation.TargetRid == TargetRid.BrowserWasm)
{
- wasmRefPaths ??= BuildReferencePaths(useWasmReferences: true);
+ wasmRefPaths ??= BuildReferencePaths(CoreLibTargetArchitecture.Wasm);
refPaths = wasmRefPaths;
}
+ else if (compilation.TargetRid == TargetRid.HostArm)
+ {
+ armRefPaths ??= BuildReferencePaths(CoreLibTargetArchitecture.Arm32);
+ refPaths = armRefPaths;
+ }
string outputPath = RunCrossgenCompilation(
testCase.Name, compilation, driver, compilation.FilePath, refPaths, assemblyPaths);
@@ -254,7 +260,7 @@ private Dictionary CompileAllAssemblies(
var paths = new Dictionary();
// Tests shouldn't require a platform-specific runtime/ref pack for Roslyn compilation
- var defaultReferences = BuildReferencePaths(useWasmReferences: false);
+ var defaultReferences = BuildReferencePaths(CoreLibTargetArchitecture.Host);
var compiler = new R2RTestCaseCompiler(defaultReferences);
foreach (var asm in assemblies)
{
@@ -365,47 +371,43 @@ private static void AddRefArgs(List args, List refPaths)
///
/// Runtime packs are used as references for both Roslyn compilation and crossgen2 compilation. This method builds the reference paths for both.
///
- /// Whether to try to use browser-wasm references. The host references are used as a fallback if browser-wasm references are not available.
- private List BuildReferencePaths(bool useWasmReferences)
+ private List BuildReferencePaths(CoreLibTargetArchitecture coreLibArch)
{
List paths;
-
- if (useWasmReferences)
+ (string runtimePackDir, string runtimePackNativeDir) = coreLibArch switch
{
- string? wasmRuntimePackDir = _paths.WasmRuntimePackDir;
- string? wasmCoreLibPath = _paths.WasmRuntimePackNativeDir is null
- ? null
- : Path.Combine(_paths.WasmRuntimePackNativeDir, "System.Private.CoreLib.dll");
-
- if (wasmRuntimePackDir is not null && wasmCoreLibPath is not null
- && File.Exists(wasmCoreLibPath) && Directory.Exists(wasmRuntimePackDir))
+ CoreLibTargetArchitecture.Host => (_paths.RuntimePackDir, _paths.RuntimePackNativeDir),
+ CoreLibTargetArchitecture.Arm32 => (_paths.ArmRuntimePackDir, _paths.ArmRuntimePackNativeDir),
+ CoreLibTargetArchitecture.Wasm => (_paths.WasmRuntimePackDir, _paths.WasmRuntimePackNativeDir),
+ _ => throw new InvalidOperationException($"Unknown CoreLibTargetArchitecture: {coreLibArch}")
+ };
+ if (!RuntimePackPathsAreValid(runtimePackDir, runtimePackNativeDir))
+ {
+ if (_paths.RequireTargetArchRuntimePack)
{
- _output.WriteLine($"Using browser-wasm runtime pack references from '{wasmRuntimePackDir}'");
- paths =
- [
- ..Directory.GetFiles(wasmRuntimePackDir, "*.dll"),
- wasmCoreLibPath
- ];
- return paths;
+ throw new InvalidOperationException($"Required runtime pack for {coreLibArch} not found: {runtimePackDir} or {runtimePackNativeDir}");
}
else
{
- Assert.False(
- _paths.RequireWasmReferences,
- "Browser-wasm framework references are required, but the browser-wasm runtime pack was not found.");
- _output.WriteLine($"Browser-wasm runtime pack references not available. Falling back to host references.");
+ _output.WriteLine($"Warning: Required runtime pack for {coreLibArch} not found: {runtimePackDir} or {runtimePackNativeDir}. Falling back to host references.");
+ (runtimePackDir, runtimePackNativeDir) = (_paths.RuntimePackDir, _paths.RuntimePackNativeDir);
}
}
- string coreLibPath = Path.Combine(_paths.RuntimePackNativeDir, "System.Private.CoreLib.dll");
+ string coreLibPath = Path.Combine(runtimePackNativeDir, "System.Private.CoreLib.dll");
Assert.True(File.Exists(coreLibPath), $"System.Private.CoreLib.dll not found: {coreLibPath}");
- Assert.True(Directory.Exists(_paths.RuntimePackDir), $"Runtime pack directory not found: {_paths.RuntimePackDir}");
+ Assert.True(Directory.Exists(runtimePackDir), $"Runtime pack directory not found: {runtimePackDir}");
paths = [
- ..Directory.GetFiles(_paths.RuntimePackDir, "*.dll"),
+ ..Directory.GetFiles(runtimePackDir, "*.dll"),
coreLibPath
];
return paths;
+
+ bool RuntimePackPathsAreValid(string runtimePackDir, string runtimePackNativeDir)
+ {
+ return Directory.Exists(runtimePackDir) && File.Exists(Path.Combine(runtimePackNativeDir, "System.Private.CoreLib.dll"));
+ }
}
}
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/TestPaths.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/TestPaths.cs
index 71661567fe2daf..f6bff9fe1b2305 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/TestPaths.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/TestPaths.cs
@@ -28,12 +28,6 @@ private static string GetRequiredConfig(string key)
?? throw new InvalidOperationException($"Missing RuntimeHostConfigurationOption '{key}'. Was the project built with the correct properties?");
}
- private static string? GetOptionalConfig(string key)
- {
- string? value = AppContext.GetData(key) as string;
- return string.IsNullOrEmpty(value) ? null : value;
- }
-
///
/// Path to the crossgen2 output directory (contains the self-contained crossgen2 executable and clrjit).
/// e.g. artifacts/bin/coreclr/linux.x64.Checked/x64/crossgen2/
@@ -58,23 +52,23 @@ private static string GetRequiredConfig(string key)
public string RuntimePackNativeDir => GetRequiredConfig("R2RTest.RuntimePackNativeDir");
///
- /// Path to the browser-wasm runtime pack managed assemblies directory, or null when the
- /// browser-wasm runtime pack has not been built/downloaded (e.g. in the default linux-x64 tools
- /// test job). When present, the wasm crossgen2 compilation references these real browser-wasm
+ /// Path to the browser-wasm runtime pack managed assemblies directory.
+ /// When present, the wasm crossgen2 compilation references these real browser-wasm
/// framework assemblies instead of the host runtime pack.
- /// e.g. artifacts/bin/microsoft.netcore.app.runtime.browser-wasm/Release/runtimes/browser-wasm/lib/net11.0/
///
- public string? WasmRuntimePackDir => GetOptionalConfig("R2RTest.WasmRuntimePackDir");
+ public string WasmRuntimePackDir => GetRequiredConfig("R2RTest.WasmRuntimePackDir");
///
/// Path to the browser-wasm runtime pack native directory (contains the wasm
- /// System.Private.CoreLib.dll), or null when not available.
- /// e.g. artifacts/bin/microsoft.netcore.app.runtime.browser-wasm/Release/runtimes/browser-wasm/native/
+ /// System.Private.CoreLib.dll).
///
- public string? WasmRuntimePackNativeDir => GetOptionalConfig("R2RTest.WasmRuntimePackNativeDir");
+ public string WasmRuntimePackNativeDir => GetRequiredConfig("R2RTest.WasmRuntimePackNativeDir");
+
+ public string ArmRuntimePackDir => GetRequiredConfig("R2RTest.ArmRuntimePackDir");
+ public string ArmRuntimePackNativeDir => GetRequiredConfig("R2RTest.ArmRuntimePackNativeDir");
- public bool RequireWasmReferences => string.Equals(
- GetOptionalConfig("R2RTest.RequireWasmReferences"),
+ public bool RequireTargetArchRuntimePack => string.Equals(
+ GetRequiredConfig("R2RTest.RequireTargetArchRuntimePack"),
bool.TrueString,
StringComparison.OrdinalIgnoreCase);
diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.CoreCLR.sfxproj b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.CoreCLR.sfxproj
index 61dcef292c508d..063aa3124cd217 100644
--- a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.CoreCLR.sfxproj
+++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.CoreCLR.sfxproj
@@ -14,8 +14,6 @@
NetCore.SharedFramework
true
true
-
- true
From 6b3b3d16c2d896c812ddcea229bb7122ce49aa10 Mon Sep 17 00:00:00 2001
From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com>
Date: Fri, 24 Jul 2026 11:02:22 -0700
Subject: [PATCH 07/17] Revert change to remove static libs from wasm runtime
pack, fix test setup, remove alljits from toolstest build
---
eng/Subsets.props | 1 -
.../TestCases/R2RTestSuites.cs | 8 +-------
.../Microsoft.NETCore.App.Runtime.CoreCLR.sfxproj | 2 ++
3 files changed, 3 insertions(+), 8 deletions(-)
diff --git a/eng/Subsets.props b/eng/Subsets.props
index 549b541d80a2c0..60061312f36f7b 100644
--- a/eng/Subsets.props
+++ b/eng/Subsets.props
@@ -383,7 +383,6 @@
- <_CrossToolSubset Condition="'$(_BuildCrossComponents)' == 'true' and '$(TargetArchitecture)' != 'wasm' and $(_subset.Contains('+clr.toolstests+'))" Include="ClrAllJitsSubset=true" />
<_CrossToolSubset Condition="'$(_BuildCrossComponents)' == 'true' and '$(TargetArchitecture)' != 'wasm' and ($(_subset.Contains('+clr.tools+')) or $(_subset.Contains('+clr.nativecorelib+')) or $(_subset.Contains('+clr.crossarchtools+')))" Include="ClrJitSubset=true" />
<_CrossToolSubset Condition="'$(_BuildCrossComponents)' == 'true' and '$(TargetArchitecture)' == 'wasm' and ($(_subset.Contains('+clr.tools+')) or $(_subset.Contains('+clr.nativecorelib+')) or $(_subset.Contains('+clr.crossarchtools+')))" Include="ClrWasmJitSubset=true" />
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs
index 1223c2f30db233..f18f14749937dd 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs
@@ -133,13 +133,7 @@ public void WasmSimdModule()
new(nameof(WasmSimdModule), [new CrossgenAssembly(wasmSimdModule)])
{
OutputFileExtension = ".wasm",
- AdditionalArgs =
- {
- "--targetarch",
- "wasm",
- "--targetos",
- "browser",
- },
+ TargetRid = TargetRid.BrowserWasm,
Validate = Validate,
},
]));
diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.CoreCLR.sfxproj b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.CoreCLR.sfxproj
index 063aa3124cd217..45a5d877f608a9 100644
--- a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.CoreCLR.sfxproj
+++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.CoreCLR.sfxproj
@@ -14,6 +14,8 @@
NetCore.SharedFramework
true
true
+
+ true
From 6302dd380223824792a3cd1ca05355ac9c40613d Mon Sep 17 00:00:00 2001
From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com>
Date: Tue, 28 Jul 2026 10:41:16 -0700
Subject: [PATCH 08/17] Compile R2R tests for the build's own target, one CI
leg per target
The R2R test suite previously compiled every shared test case for the host
(x64) and only cross-compiled two cases for arm, so a CI leg that built for a
non-host target had no assets to run against. Make every test case compile for
whatever target the outer build is producing, and run the suite once per target
in CI.
The test assembly itself always runs on the build machine, so pin it to the
host RID and keep the target out of its output path. Each root build instead
stages its own crossgen2 and shared-framework libraries into a
$(TargetOS)-$(TargetArchitecture) subdirectory of the shared output directory,
and publishes the target through runtimeconfig.json for the tests to read.
Builds for different targets accumulate side by side rather than clobbering
each other, which keeps a future all-targets-in-one-run possible.
Collapsing the output directory needs Platform=AnyCPU set before
eng/OSArch.props infers it from TargetArchitecture, hence the explicit SDK
import. Sharing the output directory in turn means IncrementalClean would treat
another target's staged files as orphans and delete them, so give each target
its own file-write log via CleanFile. The intermediate directory stays shared so
that the runtimeconfig incrementality cache is invalidated on a target switch.
Target-incompatible cases are gated with ConditionalFact rather than being
silently skipped: wasm cannot run most of them (crossgen2 asserts in the wasm
JIT, and R2R output is a wasm module rather than a PE image that
ILCompiler.Reflection.ReadyToRun can read back).
Coverage improves: 32 shared cases now cross-compile for arm where previously
only 2 did.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
eng/pipelines/runtime.yml | 66 +++++--
.../ILCompiler.ReadyToRun.Tests.csproj | 168 ++++++++++--------
.../TestCases/R2RTestSuites.cs | 82 ++++-----
.../TestCasesRunner/R2RDriver.cs | 25 +--
.../TestCasesRunner/R2RTestRunner.cs | 82 ++-------
.../TestCasesRunner/TestPaths.cs | 102 +++++++----
6 files changed, 269 insertions(+), 256 deletions(-)
diff --git a/eng/pipelines/runtime.yml b/eng/pipelines/runtime.yml
index 5dfa9e699e1aa9..42ebc00c3330b8 100644
--- a/eng/pipelines/runtime.yml
+++ b/eng/pipelines/runtime.yml
@@ -884,6 +884,10 @@ extends:
eq(stageDependencies.EvaluatePaths.evaluate_paths.outputs['SetPathVars_tools_cdac.containsChange'], true),
eq(variables['isRollingBuild'], true))
+ # The R2R test suite compiles for one target per run: the test assembly itself always runs on
+ # the build machine, while crossgen2 and the shared framework it compiles against are staged
+ # for whatever the build targets. Each leg therefore needs only its own target's artifacts,
+ # and test cases the target cannot compile are skipped rather than failed.
- template: /eng/pipelines/common/platform-matrix.yml
parameters:
jobTemplate: /eng/pipelines/common/global-build-job.yml
@@ -892,12 +896,38 @@ extends:
- linux_x64
jobParameters:
dependsOn:
- - build_browser_wasm_linux_checked_CoreCLR_ReleaseLibraries
- - build_linux_arm_checked_CoreCLR_ReleaseLibraries
- build_linux_x64_checked_CoreCLR_ReleaseLibraries
timeoutInMinutes: 120
- nameSuffix: CLR_R2R_Tests
- buildArgs: -s clr.r2rtests -lc Release -c $(_BuildConfig) -test /p:RequireTargetArchRuntimePack=true
+ nameSuffix: CLR_R2R_Tests_linux_x64
+ buildArgs: -s clr.r2rtests -os linux -arch x64 -lc Release -c $(_BuildConfig) -test
+ enablePublishTestResults: true
+ testResultsFormat: 'xunit'
+ preBuildSteps:
+ - template: /eng/pipelines/common/download-artifact-step.yml
+ parameters:
+ artifactName: CoreCLR_ReleaseLibraries_BuildArtifacts_linux_x64_Checked
+ artifactFileName: CoreCLR_ReleaseLibraries_BuildArtifacts_linux_x64_Checked$(archiveExtension)
+ unpackFolder: $(Build.SourcesDirectory)/artifacts/bin
+ cleanUnpackFolder: false
+ overwriteExistingFiles: true
+ displayName: Download linux-x64 artifacts
+ condition: >-
+ or(
+ eq(stageDependencies.EvaluatePaths.evaluate_paths.outputs['SetPathVars_coreclr.containsChange'], true),
+ eq(variables['isRollingBuild'], true))
+
+ - template: /eng/pipelines/common/platform-matrix.yml
+ parameters:
+ jobTemplate: /eng/pipelines/common/global-build-job.yml
+ buildConfig: checked
+ platforms:
+ - linux_x64
+ jobParameters:
+ dependsOn:
+ - build_browser_wasm_linux_checked_CoreCLR_ReleaseLibraries
+ timeoutInMinutes: 120
+ nameSuffix: CLR_R2R_Tests_browser_wasm
+ buildArgs: -s clr.r2rtests -os browser -arch wasm -lc Release -c $(_BuildConfig) -test
enablePublishTestResults: true
testResultsFormat: 'xunit'
preBuildSteps:
@@ -909,6 +939,26 @@ extends:
cleanUnpackFolder: false
overwriteExistingFiles: true
displayName: Download browser-wasm artifacts
+ condition: >-
+ or(
+ eq(stageDependencies.EvaluatePaths.evaluate_paths.outputs['SetPathVars_coreclr.containsChange'], true),
+ eq(variables['isRollingBuild'], true))
+
+ - template: /eng/pipelines/common/platform-matrix.yml
+ parameters:
+ jobTemplate: /eng/pipelines/common/global-build-job.yml
+ buildConfig: checked
+ platforms:
+ - linux_x64
+ jobParameters:
+ dependsOn:
+ - build_linux_arm_checked_CoreCLR_ReleaseLibraries
+ timeoutInMinutes: 120
+ nameSuffix: CLR_R2R_Tests_linux_arm
+ buildArgs: -s clr.r2rtests -os linux -arch arm -lc Release -c $(_BuildConfig) -test
+ enablePublishTestResults: true
+ testResultsFormat: 'xunit'
+ preBuildSteps:
- template: /eng/pipelines/common/download-artifact-step.yml
parameters:
artifactName: CoreCLR_ReleaseLibraries_BuildArtifacts_linux_arm_Checked
@@ -917,14 +967,6 @@ extends:
cleanUnpackFolder: false
overwriteExistingFiles: true
displayName: Download linux-arm artifacts
- - template: /eng/pipelines/common/download-artifact-step.yml
- parameters:
- artifactName: CoreCLR_ReleaseLibraries_BuildArtifacts_linux_x64_Checked
- artifactFileName: CoreCLR_ReleaseLibraries_BuildArtifacts_linux_x64_Checked$(archiveExtension)
- unpackFolder: $(Build.SourcesDirectory)/artifacts/bin
- cleanUnpackFolder: false
- overwriteExistingFiles: true
- displayName: Download linux-x64 artifacts
condition: >-
or(
eq(stageDependencies.EvaluatePaths.evaluate_paths.outputs['SetPathVars_coreclr.containsChange'], true),
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj
index 17373e0c6f8457..2b0ec747e062eb 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj
@@ -1,4 +1,15 @@
-
+
+
+
+
+ AnyCPU
+
+
+
ILCompiler.ReadyToRun.Tests
@@ -7,7 +18,21 @@
true
true
-notrait category=failing
- false
+
+ $(NETCoreSdkRuntimeIdentifier)
+ false
+
+
+
+
+ $(MSBuildProjectFile).$(TargetOS).$(TargetArchitecture).FileListAbsolute.txt
@@ -24,89 +49,84 @@
-
- Configuration=$(CoreCLRConfiguration);CopyAllJitLibrariesToAotCompilerOutput=true
+
+
+ Configuration=$(CoreCLRConfiguration);CopyAllJitLibrariesToAotCompilerOutput=true;TargetArchitecture=$(TargetArchitecture);TargetOS=$(TargetOS)
- $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'microsoft.netcore.app.runtime.browser-wasm'))
- $([MSBuild]::NormalizeDirectory('$(WasmRuntimePackRoot)', '$(LibrariesConfiguration)', 'runtimes', 'browser-wasm', 'lib', '$(NetCoreAppCurrent)'))
- $([MSBuild]::NormalizeDirectory('$(WasmRuntimePackRoot)', '$(LibrariesConfiguration)', 'runtimes', 'browser-wasm', 'native'))
- $([MSBuild]::NormalizeDirectory('$(WasmRuntimePackRoot)', 'Release', 'runtimes', 'browser-wasm', 'lib', '$(NetCoreAppCurrent)'))
- $([MSBuild]::NormalizeDirectory('$(WasmRuntimePackRoot)', 'Release', 'runtimes', 'browser-wasm', 'native'))
-
- $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'microsoft.netcore.app.runtime.$(TargetOS)-arm'))
- $([MSBuild]::NormalizeDirectory('$(ArmRuntimePackRoot)', '$(LibrariesConfiguration)', 'runtimes', '$(TargetOS)-arm', 'lib', '$(NetCoreAppCurrent)'))
- $([MSBuild]::NormalizeDirectory('$(ArmRuntimePackRoot)', '$(LibrariesConfiguration)', 'runtimes', '$(TargetOS)-arm', 'native'))
- $([MSBuild]::NormalizeDirectory('$(ArmRuntimePackRoot)', 'Release', 'runtimes', '$(TargetOS)-arm', 'lib', '$(NetCoreAppCurrent)'))
- $([MSBuild]::NormalizeDirectory('$(ArmRuntimePackRoot)', 'Release', 'runtimes', '$(TargetOS)-arm', 'native'))
+
+ $(TargetOS)-$(TargetArchitecture)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
-
- @(Crossgen2InbuildOutputItems->WithMetadataValue('Filename', 'crossgen2')->WithMetadataValue('Extension', '$(ExeSuffix)')->'%(RootDir)%(Directory)')
-
-
- $(MicrosoftNetCoreAppRuntimePackRidLibTfmDir)
-
-
- $(MicrosoftNetCoreAppRuntimePackNativeDir)
-
-
-
- $(RequireTargetArchRuntimePack)
-
-
-
- $(WasmRuntimePackDir)
-
-
- $(WasmRuntimePackNativeDir)
-
-
-
- $(ArmRuntimePackDir)
-
-
- $(ArmRuntimePackNativeDir)
-
-
-
-
- $(CoreCLRConfiguration)
-
+ <_SharedFrameworkAssembly Include="$(LibrariesSharedFrameworkBinArtifactsPath)*.dll" />
+ <_SharedFrameworkAssembly Include="$(CoreCLRArtifactsPath)IL/System.Private.CoreLib.dll" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs
index f18f14749937dd..0065e926628aa9 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs
@@ -29,7 +29,7 @@ public R2RTestSuites(ITestOutputHelper output)
_output = output;
}
- [Fact]
+ [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsNotWasmTarget))]
public void BasicCrossModuleInlining()
{
var inlineableLib = new CompiledAssembly
@@ -62,7 +62,7 @@ static void Validate(ReadyToRunReader reader)
}
}
- [Fact]
+ [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsWasmTarget))]
public void WasmWebcilModule()
{
var wasmWebcilModule = new CompiledAssembly
@@ -80,7 +80,6 @@ public void WasmWebcilModule()
// Reference the real browser-wasm framework assemblies when they are available
// (e.g. the wasm tools-test job downloads them); otherwise the runner falls back
// to host references so this test still runs everywhere.
- TargetRid = TargetRid.BrowserWasm,
Validate = Validate,
},
]));
@@ -118,7 +117,7 @@ static void Validate(ReadyToRunReader reader)
}
}
- [Fact]
+ [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsWasmTarget))]
public void WasmSimdModule()
{
var wasmSimdModule = new CompiledAssembly
@@ -133,7 +132,6 @@ public void WasmSimdModule()
new(nameof(WasmSimdModule), [new CrossgenAssembly(wasmSimdModule)])
{
OutputFileExtension = ".wasm",
- TargetRid = TargetRid.BrowserWasm,
Validate = Validate,
},
]));
@@ -193,7 +191,7 @@ static WebcilImageReader.WasmFunctionInfo ResolveWasmBody(
}
}
- [Fact]
+ [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsNotWasmTarget))]
public void RuntimeFunctionsSectionSizeExcludesSentinel()
{
var lib = new CompiledAssembly
@@ -226,7 +224,7 @@ static void Validate(ReadyToRunReader reader)
}
}
- [ConditionalFact(typeof(TestPaths), nameof(TestPaths.ArmOnHostOSSupported))]
+ [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsArmTarget))]
public void ArmThumbBitRelocationTargets()
{
var inlineableLib = new CompiledAssembly
@@ -250,7 +248,6 @@ public void ArmThumbBitRelocationTargets()
new CrossgenAssembly(inlineableLib) { Kind = Crossgen2InputKind.Reference },
])
{
- TargetRid = TargetRid.HostArm,
Validate = Validate,
},
]));
@@ -263,7 +260,7 @@ static void Validate(ReadyToRunReader reader)
}
// JitStressProcedureSplitting is only available in Debug/Checked JIT builds.
- [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsNotReleaseCoreCLR), nameof(TestPaths.ArmOnHostOSSupported))]
+ [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsNotReleaseCoreCLR), nameof(TestPaths.IsArmTarget))]
public void ArmThumbBitHotColdRuntimeFunctions()
{
var hotColdSplitting = new CompiledAssembly
@@ -277,7 +274,6 @@ public void ArmThumbBitHotColdRuntimeFunctions()
[
new(nameof(ArmThumbBitHotColdRuntimeFunctions), [new CrossgenAssembly(hotColdSplitting)])
{
- TargetRid = TargetRid.HostArm,
Options =
[
Crossgen2Option.Optimize,
@@ -299,7 +295,7 @@ static void Validate(ReadyToRunReader reader)
}
}
- [Fact]
+ [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsNotWasmTarget))]
public void TransitiveReferences()
{
var externalLib = new CompiledAssembly()
@@ -342,7 +338,7 @@ public void TransitiveReferences()
]));
}
- [Fact]
+ [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsNotWasmTarget))]
public void AsyncCrossModuleInlining()
{
var asyncInlineableLib = new CompiledAssembly
@@ -382,7 +378,7 @@ static void Validate(ReadyToRunReader reader)
}
}
- [Fact]
+ [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsNotWasmTarget))]
public void CompositeBasic()
{
var compositeLib = new CompiledAssembly
@@ -418,7 +414,7 @@ static void Validate(ReadyToRunReader reader)
}
}
- [Fact]
+ [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsNotWasmTarget))]
public void CompositeManifestAssemblyMvidsAreAligned()
{
var compositeLib = new CompiledAssembly
@@ -454,9 +450,7 @@ static void Validate(ReadyToRunReader reader)
}
}
- public static bool IsWindows => System.OperatingSystem.IsWindows();
-
- [ConditionalFact(nameof(IsWindows))]
+ [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsWindows), nameof(TestPaths.IsNotWasmTarget))]
public void CompositeManifestAssemblyMvidsArePaddedWhenPdbPresent()
{
var compositeLib = new CompiledAssembly
@@ -506,7 +500,7 @@ static void Validate(ReadyToRunReader reader)
/// boundary. When they landed on an unaligned RVA the runtime faulted on ARM32 (which does not
/// permit unaligned multi-word loads) during coreclr_initialize; x64/arm64 tolerated it.
///
- [Fact]
+ [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsNotWasmTarget))]
public void CompositeManifestSectionsAreAligned()
{
var compositeLib = new CompiledAssembly
@@ -548,7 +542,7 @@ static void Validate(ReadyToRunReader reader)
/// sections off a 4-byte boundary without the fix. Windows-only because it relies on Windows PDB
/// generation.
///
- [ConditionalFact(nameof(IsWindows))]
+ [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsWindows), nameof(TestPaths.IsNotWasmTarget))]
public void CompositeManifestSectionsArePaddedWhenPdbPresent()
{
var compositeLib = new CompiledAssembly
@@ -585,7 +579,7 @@ static void Validate(ReadyToRunReader reader)
}
}
- [Fact]
+ [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsNotWasmTarget))]
public void RuntimeAsyncMethodEmission()
{
var runtimeAsyncMethodEmission = new CompiledAssembly
@@ -623,7 +617,7 @@ static void Validate(ReadyToRunReader reader)
/// It must also strip a non-async Task-returning method whose async variant has already been
/// compiled, since the IL is no longer needed at runtime.
///
- [Fact]
+ [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsNotWasmTarget))]
public void RuntimeAsyncStripILBodiesPreservesTaskReturningIL()
{
var stripILBodies = new CompiledAssembly
@@ -682,7 +676,7 @@ static void Validate(ReadyToRunReader reader)
/// produce ContinuationLayout fixups encoding the GC ref map.
/// PR #124203: Resumption stubs for methods with suspension points.
///
- [Fact]
+ [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsNotWasmTarget))]
public void RuntimeAsyncContinuationLayout()
{
var runtimeAsyncContinuationLayout = new CompiledAssembly
@@ -721,7 +715,7 @@ static void Validate(ReadyToRunReader reader)
/// PR #125420: [ASYNC] variant generation for devirtualizable async call patterns
/// (sealed class and interface dispatch through AsyncAwareVirtualMethodResolutionAlgorithm).
///
- [Fact]
+ [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsNotWasmTarget))]
public void RuntimeAsyncDevirtualize()
{
var runtimeAsyncDevirtualize = new CompiledAssembly
@@ -756,7 +750,7 @@ static void Validate(ReadyToRunReader reader)
/// PR #124203: Async methods without yield points may omit resumption stubs.
/// Validates that no-yield async methods still produce [ASYNC] variants.
///
- [Fact]
+ [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsNotWasmTarget))]
public void RuntimeAsyncNoYield()
{
var runtimeAsyncNoYield = new CompiledAssembly
@@ -793,7 +787,7 @@ static void Validate(ReadyToRunReader reader)
/// --determinism-stress), each compiled method should have exactly one
/// ResumptionStubEntryPoint fixup.
///
- [Fact]
+ [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsNotWasmTarget))]
public void RuntimeAsyncResumptionStubFixupDedup()
{
var asm = new CompiledAssembly
@@ -836,7 +830,7 @@ static void Validate(ReadyToRunReader reader)
/// PR #121679: MutableModule async references + cross-module inlining
/// of runtime-async methods with cross-module dependency.
///
- [Fact]
+ [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsNotWasmTarget))]
public void RuntimeAsyncCrossModule()
{
var asyncDepLib = new CompiledAssembly
@@ -895,7 +889,7 @@ static void Validate(ReadyToRunReader reader)
/// Validates that inlining info (CrossModuleInlineInfo or InliningInfo2) is
/// properly populated (CompositeBasic only validates ManifestRef).
///
- [Fact]
+ [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsNotWasmTarget))]
public void CompositeCrossModuleInlining()
{
var inlineableLib = new CompiledAssembly
@@ -937,7 +931,7 @@ static void Validate(ReadyToRunReader reader)
/// assemblies does NOT produce a CrossModuleInlineInfo section. CrossModuleInlineInfo only records
/// inlining where the inlinee module is outside the compiled image's version bubble
///
- [Fact]
+ [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsNotWasmTarget))]
public void CompositeDoesNotProduceCrossModuleInliningInfo()
{
var inlineableLib = new CompiledAssembly
@@ -984,7 +978,7 @@ static void Validate(ReadyToRunReader reader)
/// comes from an assembly outside the version bubble (passed as a Reference with
/// --opt-cross-module).
///
- [Fact]
+ [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsNotWasmTarget))]
public void CompositeProducesCrossModuleInliningInfoForExternalReference()
{
var inlineableLib = new CompiledAssembly
@@ -1036,7 +1030,7 @@ static void Validate(ReadyToRunReader reader)
/// Composite mode with runtime-async methods in both assemblies.
/// Validates async variants exist in composite output.
///
- [Fact]
+ [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsNotWasmTarget))]
public void CompositeAsync()
{
var asyncCompositeLib = new CompiledAssembly
@@ -1081,7 +1075,7 @@ static void Validate(ReadyToRunReader reader)
/// Verifies that, in composite mode, awaitless async candidates ARE inlined into
/// their callers.
///
- [Fact]
+ [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsNotWasmTarget))]
public void CompositeAsyncInliningMatrix()
{
var asyncInlineCandidatesLib = new CompiledAssembly
@@ -1136,7 +1130,7 @@ static void Validate(ReadyToRunReader reader)
/// https://github.com/dotnet/runtime/pull/126904 added support for ensuring the OwningType signature modifier is emitted
/// for these methods.
///
- [Fact]
+ [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsNotWasmTarget))]
public void CompositeAsyncGenericTypes()
{
var asyncGenericTypeLib = new CompiledAssembly
@@ -1201,7 +1195,7 @@ static void Validate(ReadyToRunReader reader)
/// captures GC refs across await points. Validates that ContinuationLayout
/// fixups correctly reference cross-module types via MutableModule tokens.
///
- [Fact]
+ [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsNotWasmTarget))]
public void AsyncCrossModuleContinuation()
{
var asyncDepLibCont = new CompiledAssembly
@@ -1256,7 +1250,7 @@ static void Validate(ReadyToRunReader reader)
/// Two-step compilation: composite A+B, then non-composite C referencing A.
/// Exercises the multi-compilation model.
///
- [Fact]
+ [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsNotWasmTarget))]
public void MultiStepCompositeAndNonComposite()
{
var libA = new CompiledAssembly
@@ -1321,7 +1315,7 @@ public void MultiStepCompositeAndNonComposite()
/// Composite + runtime-async + cross-module devirtualization.
/// Interface defined in AsyncInterfaceLib, call sites in CompositeAsyncDevirtMain.
///
- [Fact]
+ [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsNotWasmTarget))]
public void CompositeAsyncDevirtualize()
{
var asyncInterfaceLib = new CompiledAssembly
@@ -1369,7 +1363,7 @@ static void Validate(ReadyToRunReader reader)
/// devirtualizes to a sealed receiver. Resolving the callee's async-variant thunk must unwrap it
/// to the underlying EcmaMethod.
///
- [Fact]
+ [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsNotWasmTarget))]
public void CompositeAsyncDevirtNonAsyncCallee()
{
// Compiled WITHOUT runtime-async so the awaited virtuals get synthesized async-variant thunks.
@@ -1416,7 +1410,7 @@ static void Validate(ReadyToRunReader reader)
/// Composite with 3 assemblies in A→B→C transitive chain.
/// Validates manifest refs for all three and transitive inlining.
///
- [Fact]
+ [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsNotWasmTarget))]
public void CompositeTransitive()
{
var externalLib = new CompiledAssembly
@@ -1464,7 +1458,7 @@ static void Validate(ReadyToRunReader reader)
/// Non-composite runtime-async + transitive cross-module inlining.
/// Chain: AsyncTransitiveMain → AsyncTransitiveLib → AsyncExternalLib.
///
- [Fact]
+ [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsNotWasmTarget))]
public void AsyncCrossModuleTransitive()
{
var asyncExternalLib = new CompiledAssembly
@@ -1525,7 +1519,7 @@ static void Validate(ReadyToRunReader reader)
/// Composite + runtime-async + transitive (3 assemblies).
/// Full combination of composite, async, and transitive references.
///
- [Fact]
+ [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsNotWasmTarget))]
public void CompositeAsyncTransitive()
{
var asyncExternalLib = new CompiledAssembly
@@ -1584,7 +1578,7 @@ static void Validate(ReadyToRunReader reader)
/// Step 1: Composite of async libs. Step 2: Non-composite consumer
/// with cross-module inlining of async methods.
///
- [Fact]
+ [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsNotWasmTarget))]
public void MultiStepCompositeAndNonCompositeAsync()
{
var asyncDepLib = new CompiledAssembly
@@ -1655,7 +1649,7 @@ public void MultiStepCompositeAndNonCompositeAsync()
/// CrossModuleInlineInfo section, exercising the absolute-index encoding
/// (not delta-encoded) for cross-module inliner entries.
///
- [Fact]
+ [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsNotWasmTarget))]
public void CrossModuleGenericMultiInliner()
{
var crossModuleGenericLib = new CompiledAssembly
@@ -1701,7 +1695,7 @@ static void Validate(ReadyToRunReader reader)
}
}
- [Fact]
+ [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsNotWasmTarget))]
public void VirtualMethodGenericsNonGVM()
{
var nonGvmLib = new CompiledAssembly
@@ -1746,7 +1740,7 @@ static void Validate(ReadyToRunReader reader)
}
}
- [Fact]
+ [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsNotWasmTarget))]
public void VirtualMethodGenericsGVM()
{
var gvmLib = new CompiledAssembly
@@ -1791,7 +1785,7 @@ static void Validate(ReadyToRunReader reader)
}
}
- [Fact]
+ [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsNotWasmTarget))]
public void VirtualMethodGenericsGenericLookup()
{
var genericLookupLib = new CompiledAssembly
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RDriver.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RDriver.cs
index 24341440e31f35..1f8a0d718f1577 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RDriver.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RDriver.cs
@@ -18,13 +18,6 @@ internal enum Crossgen2AssemblyOption
CrossModuleOptimization,
}
-internal enum CoreLibTargetArchitecture
-{
- Host,
- Arm32,
- Wasm
-}
-
internal enum Crossgen2InputKind
{
InputAssembly,
@@ -88,29 +81,23 @@ internal sealed class R2RDriver
{
private static readonly TimeSpan ProcessTimeout = TimeSpan.FromMinutes(2);
private readonly ITestOutputHelper _output;
- private readonly TestPaths _paths;
- public R2RDriver(ITestOutputHelper output, TestPaths paths)
+ public R2RDriver(ITestOutputHelper output)
{
_output = output;
- _paths = paths;
-
- if (!File.Exists(_paths.Crossgen2Exe))
- throw new FileNotFoundException($"crossgen2 executable not found at {_paths.Crossgen2Exe}");
}
///
- /// Runs crossgen2 with the given arguments.
+ /// Runs the crossgen2 that compiles for this build's target RID with the given arguments.
///
public R2RCompilationResult Compile(List args)
{
- var fullArgs = new List(args);
- return RunCrossgen2(fullArgs);
+ return RunCrossgen2(TestPaths.Crossgen2Exe, new List(args));
}
- private R2RCompilationResult RunCrossgen2(List crossgen2Args)
+ private R2RCompilationResult RunCrossgen2(string crossgen2Exe, List crossgen2Args)
{
- var psi = new ProcessStartInfo(_paths.Crossgen2Exe, crossgen2Args)
+ var psi = new ProcessStartInfo(crossgen2Exe, crossgen2Args)
{
RedirectStandardOutput = true,
RedirectStandardError = true,
@@ -124,7 +111,7 @@ private R2RCompilationResult RunCrossgen2(List crossgen2Args)
psi.Environment[envVar] = null;
}
- string commandLine = $"{_paths.Crossgen2Exe} {string.Join(" ", crossgen2Args)}";
+ string commandLine = $"{crossgen2Exe} {string.Join(" ", crossgen2Args)}";
_output.WriteLine($" crossgen2 command: {commandLine}");
using var process = Process.Start(psi)!;
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestRunner.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestRunner.cs
index ce88a5bff97241..1eccfeb0938f4c 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestRunner.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestRunner.cs
@@ -11,13 +11,6 @@
namespace ILCompiler.ReadyToRun.Tests.TestCasesRunner;
-enum TargetRid
-{
- Host,
- HostArm,
- BrowserWasm
-}
-
///
/// Describes an assembly compiled by Roslyn as part of a test case.
///
@@ -101,8 +94,6 @@ internal sealed class CrossgenCompilation(string name, List as
///
public Action? Validate { get; init; }
- public TargetRid TargetRid { get; init; } = TargetRid.Host;
-
public string Name => name;
public bool IsComposite => Options.Contains(Crossgen2Option.Composite);
@@ -185,12 +176,10 @@ public void SetOutputDir(string outputDir)
internal sealed class R2RTestRunner
{
private readonly ITestOutputHelper _output;
- private readonly TestPaths _paths;
public R2RTestRunner(ITestOutputHelper output)
{
_output = output;
- _paths = new TestPaths(output);
}
///
@@ -213,25 +202,11 @@ public void Run(R2RTestCase testCase)
var assemblyPaths = CompileAllAssemblies(assembliesToCompile);
// Step 2: Run each crossgen2 compilation and validate
- var driver = new R2RDriver(_output, _paths);
- List hostRefPaths = BuildReferencePaths(CoreLibTargetArchitecture.Host);
- List? wasmRefPaths = null;
- List? armRefPaths = null;
+ var driver = new R2RDriver(_output);
+ List refPaths = BuildReferencePaths();
foreach(var compilation in testCase.Compilations)
{
- List refPaths = hostRefPaths;
- if (compilation.TargetRid == TargetRid.BrowserWasm)
- {
- wasmRefPaths ??= BuildReferencePaths(CoreLibTargetArchitecture.Wasm);
- refPaths = wasmRefPaths;
- }
- else if (compilation.TargetRid == TargetRid.HostArm)
- {
- armRefPaths ??= BuildReferencePaths(CoreLibTargetArchitecture.Arm32);
- refPaths = armRefPaths;
- }
-
string outputPath = RunCrossgenCompilation(
testCase.Name, compilation, driver, compilation.FilePath, refPaths, assemblyPaths);
@@ -260,7 +235,7 @@ private Dictionary CompileAllAssemblies(
var paths = new Dictionary();
// Tests shouldn't require a platform-specific runtime/ref pack for Roslyn compilation
- var defaultReferences = BuildReferencePaths(CoreLibTargetArchitecture.Host);
+ var defaultReferences = BuildReferencePaths();
var compiler = new R2RTestCaseCompiler(defaultReferences);
foreach (var asm in assemblies)
{
@@ -332,13 +307,7 @@ private static string RunCrossgenCompilation(
foreach (var option in compilation.Options)
args.Add(option.ToArg());
- args.AddRange(compilation.TargetRid switch
- {
- TargetRid.Host => [],
- TargetRid.HostArm => ["--targetarch", "arm"],
- TargetRid.BrowserWasm => ["--targetos", "browser", "--targetarch", "wasm"],
- _ => throw new InvalidOperationException($"Unknown target RID: {compilation.TargetRid}")
- });
+ args.AddRange(["--targetos", TestPaths.TargetOS, "--targetarch", TestPaths.TargetArchitecture]);
// Caller-supplied raw args (for options that take values, e.g. --determinism-stress=N)
args.AddRange(compilation.AdditionalArgs);
@@ -369,45 +338,18 @@ private static void AddRefArgs(List args, List refPaths)
}
///
- /// Runtime packs are used as references for both Roslyn compilation and crossgen2 compilation. This method builds the reference paths for both.
+ /// Builds the reference paths used for both Roslyn and crossgen2 compilation, from the
+ /// shared framework IL assemblies staged for this build's target RID.
///
- private List BuildReferencePaths(CoreLibTargetArchitecture coreLibArch)
+ private static List BuildReferencePaths()
{
- List paths;
- (string runtimePackDir, string runtimePackNativeDir) = coreLibArch switch
- {
- CoreLibTargetArchitecture.Host => (_paths.RuntimePackDir, _paths.RuntimePackNativeDir),
- CoreLibTargetArchitecture.Arm32 => (_paths.ArmRuntimePackDir, _paths.ArmRuntimePackNativeDir),
- CoreLibTargetArchitecture.Wasm => (_paths.WasmRuntimePackDir, _paths.WasmRuntimePackNativeDir),
- _ => throw new InvalidOperationException($"Unknown CoreLibTargetArchitecture: {coreLibArch}")
- };
- if (!RuntimePackPathsAreValid(runtimePackDir, runtimePackNativeDir))
- {
- if (_paths.RequireTargetArchRuntimePack)
- {
- throw new InvalidOperationException($"Required runtime pack for {coreLibArch} not found: {runtimePackDir} or {runtimePackNativeDir}");
- }
- else
- {
- _output.WriteLine($"Warning: Required runtime pack for {coreLibArch} not found: {runtimePackDir} or {runtimePackNativeDir}. Falling back to host references.");
- (runtimePackDir, runtimePackNativeDir) = (_paths.RuntimePackDir, _paths.RuntimePackNativeDir);
- }
- }
-
- string coreLibPath = Path.Combine(runtimePackNativeDir, "System.Private.CoreLib.dll");
- Assert.True(File.Exists(coreLibPath), $"System.Private.CoreLib.dll not found: {coreLibPath}");
- Assert.True(Directory.Exists(runtimePackDir), $"Runtime pack directory not found: {runtimePackDir}");
+ string librariesDir = TestPaths.LibrariesDir;
- paths = [
- ..Directory.GetFiles(runtimePackDir, "*.dll"),
- coreLibPath
- ];
+ List paths = [.. Directory.GetFiles(librariesDir, "*.dll")];
+ Assert.True(
+ paths.Exists(p => Path.GetFileName(p) == "System.Private.CoreLib.dll"),
+ $"System.Private.CoreLib.dll not found in staged references: {librariesDir}");
return paths;
-
- bool RuntimePackPathsAreValid(string runtimePackDir, string runtimePackNativeDir)
- {
- return Directory.Exists(runtimePackDir) && File.Exists(Path.Combine(runtimePackNativeDir, "System.Private.CoreLib.dll"));
- }
}
}
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/TestPaths.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/TestPaths.cs
index f6bff9fe1b2305..a760287f7d8b03 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/TestPaths.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/TestPaths.cs
@@ -4,8 +4,6 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
-using System.Text.RegularExpressions;
-using Xunit.Abstractions;
namespace ILCompiler.ReadyToRun.Tests.TestCasesRunner;
@@ -13,67 +11,97 @@ namespace ILCompiler.ReadyToRun.Tests.TestCasesRunner;
/// Provides paths to build artifacts needed by the test infrastructure.
/// All paths come from RuntimeHostConfigurationOption items in the csproj.
///
-internal sealed class TestPaths
+internal static class TestPaths
{
- private readonly ITestOutputHelper _output;
-
- public TestPaths(ITestOutputHelper output)
- {
- _output = output;
- }
-
private static string GetRequiredConfig(string key)
{
return AppContext.GetData(key) as string
?? throw new InvalidOperationException($"Missing RuntimeHostConfigurationOption '{key}'. Was the project built with the correct properties?");
}
- ///
- /// Path to the crossgen2 output directory (contains the self-contained crossgen2 executable and clrjit).
- /// e.g. artifacts/bin/coreclr/linux.x64.Checked/x64/crossgen2/
- ///
- public string Crossgen2Dir => GetRequiredConfig("R2RTest.Crossgen2Dir");
-
private static string Crossgen2ExeName => RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "crossgen2.exe" : "crossgen2";
///
- /// Path to the self-contained crossgen2 executable (from crossgen2_inbuild).
+ /// The OS and architecture this root build targets, e.g. linux/x64 or
+ /// browser/wasm. Every test case compiles for this target.
///
- public string Crossgen2Exe => Path.Combine(Crossgen2Dir, Crossgen2ExeName);
+ ///
+ /// The test assembly itself always runs on the build machine; this is what crossgen2 compiles
+ /// for, which is independent of what the tests run on.
+ ///
+ public static string TargetOS => GetRequiredConfig("R2RTest.TargetOS");
+ public static string TargetArchitecture => GetRequiredConfig("R2RTest.TargetArchitecture");
+
+ private static string TargetDescription => $"{TargetOS}-{TargetArchitecture}";
+
+ public static bool IsWasmTarget => TargetArchitecture == "wasm";
///
- /// Path to the host runtime pack managed assemblies directory.
+ /// Gates the test cases that are not wasm-specific. crossgen2's wasm backend cannot yet compile
+ /// most of what these tests exercise, and the R2R images it does produce are wasm modules rather
+ /// than PE files, so cannot read
+ /// them back to validate. Test cases are therefore opt-in for wasm rather than opt-out.
///
- public string RuntimePackDir => GetRequiredConfig("R2RTest.RuntimePackDir");
+ public static bool IsNotWasmTarget => !IsWasmTarget;
+
+ public static bool IsArmTarget => TargetArchitecture is "arm" or "armel";
+
+ public static bool IsWindows => OperatingSystem.IsWindows();
///
- /// Path to the host runtime pack native directory.
+ /// Subdirectory of the output directory holding the assets this root build staged. Keyed on OS
+ /// as well as architecture so that targets sharing an architecture do not collide.
+ /// Matches the TargetAssetDirName property in the csproj.
///
- public string RuntimePackNativeDir => GetRequiredConfig("R2RTest.RuntimePackNativeDir");
+ private static string TargetAssetDirName => TargetDescription;
///
- /// Path to the browser-wasm runtime pack managed assemblies directory.
- /// When present, the wasm crossgen2 compilation references these real browser-wasm
- /// framework assemblies instead of the host runtime pack.
+ /// Path to the crossgen2 that compiles for this build's target.
///
- public string WasmRuntimePackDir => GetRequiredConfig("R2RTest.WasmRuntimePackDir");
+ ///
+ /// A root build stages the crossgen2 it built into a subdirectory named after its target,
+ /// so builds for different targets accumulate rather than clobber each other.
+ /// Only the subdirectory for this build's own target is ever read: a crossgen2 built for one
+ /// target does not carry the cross-targeting JIT for any other, so substituting a different
+ /// one either fails with a DllNotFoundException or silently compiles for the wrong
+ /// architecture.
+ ///
+ public static string Crossgen2Exe
+ {
+ get
+ {
+ string exe = Path.Combine(AppContext.BaseDirectory, TargetAssetDirName, "crossgen2", Crossgen2ExeName);
+ if (!File.Exists(exe))
+ {
+ throw new FileNotFoundException(
+ $"No crossgen2 staged for {TargetDescription} at '{exe}'. Build the clr and libs subsets for " +
+ $"{TargetDescription}, then rebuild this test project.");
+ }
+
+ return exe;
+ }
+ }
///
- /// Path to the browser-wasm runtime pack native directory (contains the wasm
- /// System.Private.CoreLib.dll).
+ /// Directory holding the shared framework IL assemblies crossgen2 compiles against.
///
- public string WasmRuntimePackNativeDir => GetRequiredConfig("R2RTest.WasmRuntimePackNativeDir");
-
- public string ArmRuntimePackDir => GetRequiredConfig("R2RTest.ArmRuntimePackDir");
- public string ArmRuntimePackNativeDir => GetRequiredConfig("R2RTest.ArmRuntimePackNativeDir");
+ public static string LibrariesDir
+ {
+ get
+ {
+ string dir = Path.Combine(AppContext.BaseDirectory, TargetAssetDirName, "libraries");
+ if (!Directory.Exists(dir))
+ {
+ throw new DirectoryNotFoundException(
+ $"No shared framework assemblies staged for {TargetDescription} at '{dir}'. Build the clr and " +
+ $"libs subsets for {TargetDescription}, then rebuild this test project.");
+ }
- public bool RequireTargetArchRuntimePack => string.Equals(
- GetRequiredConfig("R2RTest.RequireTargetArchRuntimePack"),
- bool.TrueString,
- StringComparison.OrdinalIgnoreCase);
+ return dir;
+ }
+ }
public static string CoreCLRConfiguration => GetRequiredConfig("R2RTest.CoreCLRConfiguration");
public static bool IsReleaseCoreCLR => string.Equals(CoreCLRConfiguration, "Release", StringComparison.OrdinalIgnoreCase);
public static bool IsNotReleaseCoreCLR => !IsReleaseCoreCLR;
- public static bool ArmOnHostOSSupported => RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
}
From a40e9a73eaacb7b568577cc5ff7af71e6406c2b4 Mon Sep 17 00:00:00 2001
From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com>
Date: Tue, 28 Jul 2026 10:54:30 -0700
Subject: [PATCH 09/17] Give the R2R tests a per-target output directory
Follow the layout the sibling AOT tool projects already use instead of
collapsing every target into one output directory. crossgen2_inbuild.csproj
pins RuntimeIdentifier to the host RID so the tool runs on the build machine
while still being written to a per-target path; do the same here, and stage the
target's crossgen2 and shared-framework libraries directly beside the test
assembly rather than under a target-named subdirectory.
This drops the Platform=AnyCPU pin and with it the explicit SDK import that
existed only because Platform had to be set before Directory.Build.props was
imported, the per-target CleanFile that kept IncrementalClean from deleting
another target's staged files out of the shared output directory, and the
TargetAssetDirName indirection on both the MSBuild and C# sides.
PlatformTarget still has to be AnyCPU: $(Platform) otherwise defaults it to the
target architecture, which the SDK rejects as incompatible with the host RID
(NETSDK1032) when building for arm. crossgen2.props and the other AOT tool
projects all set it for the same reason.
A single shared output directory would let one run exercise every target at
once, which is worth having, but nothing needs it yet. Restore it when that
run actually exists.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
.../ILCompiler.ReadyToRun.Tests.csproj | 55 ++++---------------
.../TestCasesRunner/TestPaths.cs | 20 ++-----
2 files changed, 16 insertions(+), 59 deletions(-)
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj
index 2b0ec747e062eb..33b540d2fcadc4 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj
@@ -1,15 +1,4 @@
-
-
-
-
- AnyCPU
-
-
-
+
ILCompiler.ReadyToRun.Tests
@@ -19,20 +8,13 @@
true
-notrait category=failing
+ output path, the same way crossgen2_inbuild.csproj does. PlatformTarget must be AnyCPU to
+ go with it: $(Platform) defaults it to the target architecture, which the SDK then rejects
+ as incompatible with the host RID (NETSDK1032). The sibling AOT tool projects, including
+ crossgen2.props, all do the same. -->
$(NETCoreSdkRuntimeIdentifier)
false
-
-
-
-
- $(MSBuildProjectFile).$(TargetOS).$(TargetArchitecture).FileListAbsolute.txt
+ AnyCPU
@@ -57,19 +39,7 @@
-
-
- $(TargetOS)-$(TargetArchitecture)
-
-
-
+ GenerateBuildRuntimeConfigurationFiles gets to run, so values added from a target are
+ invisible to it and a stale runtimeconfig would be silently reused. -->
@@ -127,6 +96,4 @@
-
-
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/TestPaths.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/TestPaths.cs
index a760287f7d8b03..2265d04fbc3df1 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/TestPaths.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/TestPaths.cs
@@ -48,29 +48,19 @@ private static string GetRequiredConfig(string key)
public static bool IsWindows => OperatingSystem.IsWindows();
- ///
- /// Subdirectory of the output directory holding the assets this root build staged. Keyed on OS
- /// as well as architecture so that targets sharing an architecture do not collide.
- /// Matches the TargetAssetDirName property in the csproj.
- ///
- private static string TargetAssetDirName => TargetDescription;
-
///
/// Path to the crossgen2 that compiles for this build's target.
///
///
- /// A root build stages the crossgen2 it built into a subdirectory named after its target,
- /// so builds for different targets accumulate rather than clobber each other.
- /// Only the subdirectory for this build's own target is ever read: a crossgen2 built for one
- /// target does not carry the cross-targeting JIT for any other, so substituting a different
- /// one either fails with a DllNotFoundException or silently compiles for the wrong
- /// architecture.
+ /// A build stages the crossgen2 it built next to the test assembly. A crossgen2 built for one
+ /// target does not carry the cross-targeting JIT for any other, so substituting a different one
+ /// either fails with a DllNotFoundException or silently compiles for the wrong architecture.
///
public static string Crossgen2Exe
{
get
{
- string exe = Path.Combine(AppContext.BaseDirectory, TargetAssetDirName, "crossgen2", Crossgen2ExeName);
+ string exe = Path.Combine(AppContext.BaseDirectory, "crossgen2", Crossgen2ExeName);
if (!File.Exists(exe))
{
throw new FileNotFoundException(
@@ -89,7 +79,7 @@ public static string LibrariesDir
{
get
{
- string dir = Path.Combine(AppContext.BaseDirectory, TargetAssetDirName, "libraries");
+ string dir = Path.Combine(AppContext.BaseDirectory, "libraries");
if (!Directory.Exists(dir))
{
throw new DirectoryNotFoundException(
From cfd0352f9c4269ddd13e1a161dcbd80e0fac2914 Mon Sep 17 00:00:00 2001
From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com>
Date: Tue, 28 Jul 2026 11:00:54 -0700
Subject: [PATCH 10/17] Key the R2R test output directory on the target RID
Arcade lays projects out under $(PlatformName), which eng/OSArch.props sets to
the bare target architecture. That cannot distinguish browser-wasm from
wasi-wasm, or linux-x64 from windows-x64, so two such targets built in the same
tree would overwrite each other's staged crossgen2 and libraries.
src/coreclr/Directory.Build.props already makes this correction for the
intermediate directory, where it uses $(TargetOS).$(TargetArchitecture) rather
than $(PlatformName). Do the same for the output directory here, using the
target RID.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
.../ILCompiler.ReadyToRun.Tests.csproj | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj
index 33b540d2fcadc4..997a07e70e7ed8 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj
@@ -15,6 +15,11 @@
$(NETCoreSdkRuntimeIdentifier)
false
AnyCPU
+
+ $(BaseOutputPath)$(TargetRid)\$(Configuration)\
From 72aae643c27c68926877243dd74b7b16444e0b3c Mon Sep 17 00:00:00 2001
From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com>
Date: Tue, 28 Jul 2026 12:15:02 -0700
Subject: [PATCH 11/17] Make r2rtests build for a specific target without
requiring host-machine-targeting artifacts
---
.../ILCompiler.ReadyToRun.Tests.csproj | 54 ++++---------------
.../TestCases/R2RTestSuites.cs | 7 +--
.../TestCasesRunner/R2RTestRunner.cs | 25 +++++----
.../TestCasesRunner/TestPaths.cs | 2 +-
4 files changed, 26 insertions(+), 62 deletions(-)
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj
index 997a07e70e7ed8..a3fdd76f9b1295 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj
@@ -1,25 +1,22 @@
+
+
+
ILCompiler.ReadyToRun.Tests
$(NetCoreAppToolCurrent)
enable
- true
- true
-notrait category=failing
-
+
+ AnyCPU
$(NETCoreSdkRuntimeIdentifier)
false
- AnyCPU
-
- $(BaseOutputPath)$(TargetRid)\$(Configuration)\
+
+ $(BaseOutputPath)$(Configuration)\$(CoreCLRConfiguration)_CLR\$(TargetRid)\
@@ -27,8 +24,6 @@
-
@@ -36,33 +31,17 @@
-
Configuration=$(CoreCLRConfiguration);CopyAllJitLibrariesToAotCompilerOutput=true;TargetArchitecture=$(TargetArchitecture);TargetOS=$(TargetOS)
-
-
-
-
-
-
-
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs
index 0065e926628aa9..3af4dc9de1868f 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs
@@ -77,9 +77,6 @@ public void WasmWebcilModule()
new(nameof(WasmWebcilModule), [new CrossgenAssembly(wasmWebcilModule)])
{
OutputFileExtension = ".wasm",
- // Reference the real browser-wasm framework assemblies when they are available
- // (e.g. the wasm tools-test job downloads them); otherwise the runner falls back
- // to host references so this test still runs everywhere.
Validate = Validate,
},
]));
@@ -450,7 +447,7 @@ static void Validate(ReadyToRunReader reader)
}
}
- [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsWindows), nameof(TestPaths.IsNotWasmTarget))]
+ [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsWindowsTarget))]
public void CompositeManifestAssemblyMvidsArePaddedWhenPdbPresent()
{
var compositeLib = new CompiledAssembly
@@ -542,7 +539,7 @@ static void Validate(ReadyToRunReader reader)
/// sections off a 4-byte boundary without the fix. Windows-only because it relies on Windows PDB
/// generation.
///
- [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsWindows), nameof(TestPaths.IsNotWasmTarget))]
+ [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsWindowsTarget))]
public void CompositeManifestSectionsArePaddedWhenPdbPresent()
{
var compositeLib = new CompiledAssembly
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestRunner.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestRunner.cs
index 1eccfeb0938f4c..6ade70213e235a 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestRunner.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestRunner.cs
@@ -208,7 +208,7 @@ public void Run(R2RTestCase testCase)
foreach(var compilation in testCase.Compilations)
{
string outputPath = RunCrossgenCompilation(
- testCase.Name, compilation, driver, compilation.FilePath, refPaths, assemblyPaths);
+ testCase.Name, compilation, driver, compilation.FilePath, assemblyPaths);
if (compilation.Validate is not null)
{
@@ -234,7 +234,6 @@ private Dictionary CompileAllAssemblies(
{
var paths = new Dictionary();
- // Tests shouldn't require a platform-specific runtime/ref pack for Roslyn compilation
var defaultReferences = BuildReferencePaths();
var compiler = new R2RTestCaseCompiler(defaultReferences);
foreach (var asm in assemblies)
@@ -271,7 +270,6 @@ private static string RunCrossgenCompilation(
CrossgenCompilation compilation,
R2RDriver driver,
string outputFile,
- List refPaths,
Dictionary assemblyPaths)
{
var args = new List();
@@ -312,8 +310,8 @@ private static string RunCrossgenCompilation(
// Caller-supplied raw args (for options that take values, e.g. --determinism-stress=N)
args.AddRange(compilation.AdditionalArgs);
- // Global refs (runtime pack + System.Private.CoreLib)
- AddRefArgs(args, refPaths);
+ // Global refs (shared framework + System.Private.CoreLib)
+ AddRefArgs(args);
EnsureDirectoryExists(Path.GetDirectoryName(outputFile));
@@ -328,18 +326,19 @@ private static string RunCrossgenCompilation(
return outputFile;
}
- private static void AddRefArgs(List args, List refPaths)
+ ///
+ /// Adds the shared framework references for crossgen2 as a single directory wildcard.
+ ///
+ private static void AddRefArgs(List args)
{
- foreach (string refPath in refPaths)
- {
- args.Add("-r");
- args.Add(refPath);
- }
+ args.Add("-r");
+ args.Add(Path.Combine(TestPaths.LibrariesDir, "*.dll"));
}
///
- /// Builds the reference paths used for both Roslyn and crossgen2 compilation, from the
- /// shared framework IL assemblies staged for this build's target RID.
+ /// Builds the explicit reference paths used for Roslyn compilation and for resolving assemblies
+ /// when reading back an R2R image, from the shared framework IL assemblies staged for this
+ /// build's target RID. crossgen2 takes the same assemblies as a wildcard; see .
///
private static List BuildReferencePaths()
{
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/TestPaths.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/TestPaths.cs
index 2265d04fbc3df1..0d57a97a99205c 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/TestPaths.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/TestPaths.cs
@@ -46,7 +46,7 @@ private static string GetRequiredConfig(string key)
public static bool IsArmTarget => TargetArchitecture is "arm" or "armel";
- public static bool IsWindows => OperatingSystem.IsWindows();
+ public static bool IsWindowsTarget => TargetOS is "windows" or "win";
///
/// Path to the crossgen2 that compiles for this build's target.
From 443385c4c349b4e1a745437a5ffd12b308dec9f2 Mon Sep 17 00:00:00 2001
From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com>
Date: Wed, 29 Jul 2026 10:41:10 -0700
Subject: [PATCH 12/17] Add an ios-arm64 R2R test leg
Adds a fourth CLR_R2R_Tests leg that compiles the crossgen2 test suite for
ios-arm64, exercising the Apple ARM64 ABI codegen path. The same input
produces a different R2R image for --targetos ios (74,752 bytes) than for
--targetos linux (74,240 bytes) at the same arch, so this is not redundant
with the existing linux-arm leg.
The leg runs on a linux_x64 agent with no Apple SDK. Unlike the other three
legs it builds its inputs from source rather than downloading a prebuilt
artifact, because the iOS CoreCLR jobs run on macOS pools and their cross
components are Mach-O.
Two details make this work:
- clr.crossarchtools targets the build host, not the build target.
build-runtime.sh passes __HostOS as build_native's targetOS, so the cross
components configure for linux and land in ios.arm64.Checked/x64/.
libs.sfx is used rather than libs, since libs.native does need the Apple SDK.
- The subsets are split across two build invocations. clr.r2rtests is
declared ahead of libs.sfx in Subsets.props, and Build.proj is a Traversal
over ProjectToBuild in declaration order, so a single invocation would run
the tests before the shared framework exists.
Also drops the hardcoded --targetos/--targetarch overrides from the two
StripILBodies test cases so they compile for the build's own target, and
gates AppleMobileStripILBodiesUsesFixedInstructionSet on ios-arm64.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
eng/pipelines/runtime.yml | 28 +++++++++++++++++++
.../TestCases/R2RTestSuites.cs | 4 +--
.../TestCasesRunner/TestPaths.cs | 2 ++
3 files changed, 31 insertions(+), 3 deletions(-)
diff --git a/eng/pipelines/runtime.yml b/eng/pipelines/runtime.yml
index 00e72d68bc4b95..b09f3f7cd45a3a 100644
--- a/eng/pipelines/runtime.yml
+++ b/eng/pipelines/runtime.yml
@@ -972,6 +972,34 @@ extends:
eq(stageDependencies.EvaluatePaths.evaluate_paths.outputs['SetPathVars_coreclr.containsChange'], true),
eq(variables['isRollingBuild'], true))
+ - template: /eng/pipelines/common/platform-matrix.yml
+ parameters:
+ jobTemplate: /eng/pipelines/common/global-build-job.yml
+ buildConfig: checked
+ platforms:
+ - linux_x64
+ jobParameters:
+ timeoutInMinutes: 120
+ nameSuffix: CLR_R2R_Tests_ios_arm64
+ # No prebuilt artifact can supply this leg: the iOS jobs run on macOS, so their cross
+ # components are Mach-O and unusable here. The cross components are built from source
+ # instead - they target the build host (linux-x64), not iOS, so no Apple SDK is needed.
+ # libs.sfx rather than libs, because libs.native does require the Apple SDK.
+ buildArgs: -s clr.crossarchtools+clr.corelib+libs.sfx -os ios -arch arm64 -rf CoreCLR -lc Release -c $(_BuildConfig)
+ enablePublishTestResults: true
+ testResultsFormat: 'xunit'
+ postBuildSteps:
+ # A separate invocation because clr.r2rtests is ordered ahead of libs.sfx in
+ # Subsets.props, so a single build would run the tests before their inputs exist.
+ - template: /eng/pipelines/common/templates/global-build-step.yml
+ parameters:
+ buildArgs: -s clr.r2rtests -os ios -arch arm64 -rf CoreCLR -lc Release -c $(_BuildConfig) -test
+ displayName: Build and run R2R tests
+ condition: >-
+ or(
+ eq(stageDependencies.EvaluatePaths.evaluate_paths.outputs['SetPathVars_coreclr.containsChange'], true),
+ eq(variables['isRollingBuild'], true))
+
#
# Build CrossDacs
#
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs
index c2e13e2fd185e5..2f27ba9bcae801 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs
@@ -634,7 +634,6 @@ public void RuntimeAsyncStripILBodiesPreservesTaskReturningIL()
new(nameof(RuntimeAsyncStripILBodiesPreservesTaskReturningIL), [new CrossgenAssembly(stripILBodies)])
{
Options = [Crossgen2Option.Composite, Crossgen2Option.Optimize, Crossgen2Option.StripILBodies],
- AdditionalArgs = ["--targetarch:x64"],
Validate = Validate,
},
]));
@@ -670,7 +669,7 @@ static void Validate(ReadyToRunReader reader)
}
}
- [Fact]
+ [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsIosArm64Target))]
public void AppleMobileStripILBodiesUsesFixedInstructionSet()
{
var stripILBodies = new CompiledAssembly
@@ -690,7 +689,6 @@ public void AppleMobileStripILBodiesUsesFixedInstructionSet()
new(nameof(AppleMobileStripILBodiesUsesFixedInstructionSet), [new CrossgenAssembly(stripILBodies)])
{
Options = [Crossgen2Option.Composite, Crossgen2Option.Optimize, Crossgen2Option.StripILBodies],
- AdditionalArgs = ["--targetos:ios", "--targetarch:arm64"],
Validate = Validate,
},
]));
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/TestPaths.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/TestPaths.cs
index 282b988916c43e..0a2b64327f8e71 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/TestPaths.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/TestPaths.cs
@@ -47,6 +47,8 @@ private static string GetRequiredConfig(string key)
public static bool IsWindowsTarget => TargetOS is "windows" or "win";
+ public static bool IsIosArm64Target => TargetOS is "ios" && TargetArchitecture is "arm64";
+
///
/// Path to the crossgen2 that compiles for this build's target.
///
From b90057a29390d60e1982181e67811c15b9186551 Mon Sep 17 00:00:00 2001
From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com>
Date: Wed, 29 Jul 2026 11:22:41 -0700
Subject: [PATCH 13/17] Address review feedback: gate --pdb tests on the host,
add a Windows leg
The two CompositeManifest*ArePaddedWhenPdbPresent cases were switched from a
host-OS gate to a target-OS gate when the suite became per-target. Both
constraints are actually required, for different reasons:
- crossgen2.props stages Microsoft.DiaSymReader.Native only when crossgen2's
own target is Windows, so a non-Windows target never has the library at all.
- That library is a Windows binary, so even when staged it can only load on a
Windows host. Compiling with --pdb on Linux fails with a DllNotFoundException
before writing anything.
Gate on both. Gating on the target alone (as before this commit) would let the
tests run on a Linux host and fail; gating on the host alone would let them run
on a Windows host cross-targeting wasm or iOS, where the library is absent.
With every leg running on a Linux host, these two cases were being skipped
everywhere, so add a windows-x64 leg to restore the coverage. It also gives the
per-target test infrastructure its first Windows run, covering the command-line
length limit that the reference-argument wildcard exists for.
Also refresh two stale doc comments in TestPaths that still described the
pre-per-target artifact probing.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
eng/pipelines/runtime.yml | 30 +++++++++++++++++
.../TestCases/R2RTestSuites.cs | 13 +++++---
.../TestCasesRunner/TestPaths.cs | 32 ++++++-------------
3 files changed, 48 insertions(+), 27 deletions(-)
diff --git a/eng/pipelines/runtime.yml b/eng/pipelines/runtime.yml
index b09f3f7cd45a3a..4e452a6a71aefc 100644
--- a/eng/pipelines/runtime.yml
+++ b/eng/pipelines/runtime.yml
@@ -1000,6 +1000,36 @@ extends:
eq(stageDependencies.EvaluatePaths.evaluate_paths.outputs['SetPathVars_coreclr.containsChange'], true),
eq(variables['isRollingBuild'], true))
+ # The only leg with a Windows host, which the two --pdb test cases require: crossgen2 writes
+ # Windows PDBs through Microsoft.DiaSymReader.Native, which is restored for Windows only.
+ - template: /eng/pipelines/common/platform-matrix.yml
+ parameters:
+ jobTemplate: /eng/pipelines/common/global-build-job.yml
+ buildConfig: checked
+ platforms:
+ - windows_x64
+ jobParameters:
+ dependsOn:
+ - build_windows_x64_checked_CoreCLR_ReleaseLibraries
+ timeoutInMinutes: 120
+ nameSuffix: CLR_R2R_Tests_windows_x64
+ buildArgs: -s clr.r2rtests -os windows -arch x64 -lc Release -c $(_BuildConfig) -test
+ enablePublishTestResults: true
+ testResultsFormat: 'xunit'
+ preBuildSteps:
+ - template: /eng/pipelines/common/download-artifact-step.yml
+ parameters:
+ artifactName: CoreCLR_ReleaseLibraries_BuildArtifacts_windows_x64_Checked
+ artifactFileName: CoreCLR_ReleaseLibraries_BuildArtifacts_windows_x64_Checked$(archiveExtension)
+ unpackFolder: $(Build.SourcesDirectory)/artifacts/bin
+ cleanUnpackFolder: false
+ overwriteExistingFiles: true
+ displayName: Download windows-x64 artifacts
+ condition: >-
+ or(
+ eq(stageDependencies.EvaluatePaths.evaluate_paths.outputs['SetPathVars_coreclr.containsChange'], true),
+ eq(variables['isRollingBuild'], true))
+
#
# Build CrossDacs
#
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs
index 2f27ba9bcae801..fea166a7bd9739 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs
@@ -447,7 +447,11 @@ static void Validate(ReadyToRunReader reader)
}
}
- [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsWindowsTarget))]
+ ///
+ /// Requires a Windows host for --pdb (see ), and a
+ /// Windows target because the padding being verified is in the composite PE image.
+ ///
+ [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsWindowsHost), nameof(TestPaths.IsWindowsTarget))]
public void CompositeManifestAssemblyMvidsArePaddedWhenPdbPresent()
{
var compositeLib = new CompiledAssembly
@@ -536,10 +540,11 @@ static void Validate(ReadyToRunReader reader)
///
/// Complements using the same trigger as the
/// MVID-table test: --pdb emits an odd-sized debug directory section that shifts the manifest
- /// sections off a 4-byte boundary without the fix. Windows-only because it relies on Windows PDB
- /// generation.
+ /// sections off a 4-byte boundary without the fix. Requires a Windows host for --pdb
+ /// (see ), and a Windows target because the padding being
+ /// verified is in the composite PE image.
///
- [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsWindowsTarget))]
+ [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsWindowsHost), nameof(TestPaths.IsWindowsTarget))]
public void CompositeManifestSectionsArePaddedWhenPdbPresent()
{
var compositeLib = new CompiledAssembly
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/TestPaths.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/TestPaths.cs
index 0a2b64327f8e71..8df61102255e4f 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/TestPaths.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/TestPaths.cs
@@ -8,8 +8,8 @@
namespace ILCompiler.ReadyToRun.Tests.TestCasesRunner;
///
-/// Provides paths to build artifacts needed by the test infrastructure.
-/// All paths come from RuntimeHostConfigurationOption items in the csproj.
+/// Provides paths to build artifacts needed by the test infrastructure, and the target and host
+/// predicates that gate test cases.
///
internal static class TestPaths
{
@@ -21,26 +21,15 @@ private static string GetRequiredConfig(string key)
private static string Crossgen2ExeName => RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "crossgen2.exe" : "crossgen2";
- ///
- /// Path to System.Private.CoreLib.dll. It lives in the runtime pack native/ dir in full builds
- /// (placed by externals.csproj BinPlace during libs.pretest), but partial builds that skip
- /// libs.pretest only have it in the CoreCLR artifacts directory.
- ///
public static string SystemPrivateCoreLibPath => Path.Combine(LibrariesDir, "System.Private.CoreLib.dll");
public static string TargetOS => GetRequiredConfig("R2RTest.TargetOS");
public static string TargetArchitecture => GetRequiredConfig("R2RTest.TargetArchitecture");
- private static string TargetDescription => $"{TargetOS}-{TargetArchitecture}";
+ private static string TargetRid => $"{TargetOS}-{TargetArchitecture}";
public static bool IsWasmTarget => TargetArchitecture == "wasm";
- ///
- /// Gates the test cases that are not wasm-specific. crossgen2's wasm backend cannot yet compile
- /// most of what these tests exercise, and the R2R images it does produce are wasm modules rather
- /// than PE files, so cannot read
- /// them back to validate. Test cases are therefore opt-in for wasm rather than opt-out.
- ///
public static bool IsNotWasmTarget => !IsWasmTarget;
public static bool IsArmTarget => TargetArchitecture is "arm" or "armel";
@@ -49,14 +38,11 @@ private static string GetRequiredConfig(string key)
public static bool IsIosArm64Target => TargetOS is "ios" && TargetArchitecture is "arm64";
+ public static bool IsWindowsHost => OperatingSystem.IsWindows();
+
///
/// Path to the crossgen2 that compiles for this build's target.
///
- ///
- /// A build stages the crossgen2 it built next to the test assembly. A crossgen2 built for one
- /// target does not carry the cross-targeting JIT for any other, so substituting a different one
- /// either fails with a DllNotFoundException or silently compiles for the wrong architecture.
- ///
public static string Crossgen2Exe
{
get
@@ -65,8 +51,8 @@ public static string Crossgen2Exe
if (!File.Exists(exe))
{
throw new FileNotFoundException(
- $"No crossgen2 staged for {TargetDescription} at '{exe}'. Build the clr and libs subsets for " +
- $"{TargetDescription}, then rebuild this test project.");
+ $"No crossgen2 staged for {TargetRid} at '{exe}'. Build the clr and libs subsets for " +
+ $"{TargetRid}, then rebuild this test project.");
}
return exe;
@@ -84,8 +70,8 @@ public static string LibrariesDir
if (!Directory.Exists(dir))
{
throw new DirectoryNotFoundException(
- $"No shared framework assemblies staged for {TargetDescription} at '{dir}'. Build the clr and " +
- $"libs subsets for {TargetDescription}, then rebuild this test project.");
+ $"No shared framework assemblies staged for {TargetRid} at '{dir}'. Build the clr and " +
+ $"libs subsets for {TargetRid}, then rebuild this test project.");
}
return dir;
From c586d573decf3fb02bab327441b5f743deb40fd7 Mon Sep 17 00:00:00 2001
From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com>
Date: Wed, 29 Jul 2026 13:05:01 -0700
Subject: [PATCH 14/17] Restrict WasmArgumentLayoutTests to wasm targets
The class builds a wasm type system context unconditionally, but reads
System.Private.CoreLib from whatever the outer build targets. On a non-wasm
leg it was therefore computing wasm argument layouts from an ios-arm64 or
linux-x64 corelib, which passes without testing anything meaningful.
ConditionalClass filters the whole class at discovery, so the 20 theory cases
no longer appear at all outside the wasm leg (59 -> 39 cases there) and still
run and pass on it.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
.../ILCompiler.ReadyToRun.Tests/WasmArgumentLayoutTests.cs | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/WasmArgumentLayoutTests.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/WasmArgumentLayoutTests.cs
index e617c30d1cdaf3..3ace6b5af8c7eb 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/WasmArgumentLayoutTests.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/WasmArgumentLayoutTests.cs
@@ -25,8 +25,12 @@ namespace ILCompiler.ReadyToRun.Tests;
///
/// Unit tests for the wasm argument layout crossgen2 computes. These drive the compiler's type
-/// system directly, so they need neither a wasm JIT nor a runtime to execute against.
+/// system directly, so they need neither a wasm JIT nor a runtime to execute against. They do
+/// need a wasm System.Private.CoreLib, though: the type system context they build is always a
+/// wasm one, but the corelib it reads comes from whatever the outer build targets, so on any
+/// other target they would be computing wasm layouts from the wrong assembly.
///
+[ConditionalClass(typeof(TestPaths), nameof(TestPaths.IsWasmTarget))]
public class WasmArgumentLayoutTests
{
private const string Vector128OfT = "Vector128`1";
From af17aab6aec04859e584d04f7b5247bdad745d39 Mon Sep 17 00:00:00 2001
From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com>
Date: Wed, 29 Jul 2026 17:01:27 -0700
Subject: [PATCH 15/17] Pass the R2R test target via
_osParameter/_archParameter instead of buildArgs
The R2R test legs all restated -os/-arch in buildArgs even though
global-build-step.yml already passes $(_osParameter)/$(_archParameter)
to the build script, so every leg emitted both and relied on last-wins
argument parsing.
Override those two variables per leg instead. The job-level variables
list is expanded after global-build-job.yml defines its defaults, so a
later definition wins - the same mechanism global-build-job.yml already
uses to redefine _osParameter for linux-bionic. The iOS leg gets both
of its build invocations covered by the one override.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
eng/pipelines/runtime.yml | 25 +++++++++++++++++++------
1 file changed, 19 insertions(+), 6 deletions(-)
diff --git a/eng/pipelines/runtime.yml b/eng/pipelines/runtime.yml
index 6821ca3a6c5eae..4df53112406fc1 100644
--- a/eng/pipelines/runtime.yml
+++ b/eng/pipelines/runtime.yml
@@ -897,7 +897,7 @@ extends:
- build_linux_x64_checked_CoreCLR_ReleaseLibraries
timeoutInMinutes: 120
nameSuffix: CLR_R2R_Tests_linux_x64
- buildArgs: -s clr.r2rtests -os linux -arch x64 -lc Release -c $(_BuildConfig) -test
+ buildArgs: -s clr.r2rtests -lc Release -c $(_BuildConfig) -test
enablePublishTestResults: true
testResultsFormat: 'xunit'
preBuildSteps:
@@ -920,12 +920,17 @@ extends:
buildConfig: checked
platforms:
- linux_x64
+ variables:
+ - name: _osParameter
+ value: -os browser
+ - name: _archParameter
+ value: -arch wasm
jobParameters:
dependsOn:
- build_browser_wasm_linux_checked_CoreCLR_ReleaseLibraries
timeoutInMinutes: 120
nameSuffix: CLR_R2R_Tests_browser_wasm
- buildArgs: -s clr.r2rtests -os browser -arch wasm -lc Release -c $(_BuildConfig) -test
+ buildArgs: -s clr.r2rtests -lc Release -c $(_BuildConfig) -test
enablePublishTestResults: true
testResultsFormat: 'xunit'
preBuildSteps:
@@ -948,12 +953,15 @@ extends:
buildConfig: checked
platforms:
- linux_x64
+ variables:
+ - name: _archParameter
+ value: -arch arm
jobParameters:
dependsOn:
- build_linux_arm_checked_CoreCLR_ReleaseLibraries
timeoutInMinutes: 120
nameSuffix: CLR_R2R_Tests_linux_arm
- buildArgs: -s clr.r2rtests -os linux -arch arm -lc Release -c $(_BuildConfig) -test
+ buildArgs: -s clr.r2rtests -lc Release -c $(_BuildConfig) -test
enablePublishTestResults: true
testResultsFormat: 'xunit'
preBuildSteps:
@@ -976,6 +984,11 @@ extends:
buildConfig: checked
platforms:
- linux_x64
+ variables:
+ - name: _osParameter
+ value: -os ios
+ - name: _archParameter
+ value: -arch arm64
jobParameters:
timeoutInMinutes: 120
nameSuffix: CLR_R2R_Tests_ios_arm64
@@ -983,7 +996,7 @@ extends:
# components are Mach-O and unusable here. The cross components are built from source
# instead - they target the build host (linux-x64), not iOS, so no Apple SDK is needed.
# libs.sfx rather than libs, because libs.native does require the Apple SDK.
- buildArgs: -s clr.crossarchtools+clr.corelib+libs.sfx -os ios -arch arm64 -rf CoreCLR -lc Release -c $(_BuildConfig)
+ buildArgs: -s clr.crossarchtools+clr.corelib+libs.sfx -rf CoreCLR -lc Release -c $(_BuildConfig)
enablePublishTestResults: true
testResultsFormat: 'xunit'
postBuildSteps:
@@ -991,7 +1004,7 @@ extends:
# Subsets.props, so a single build would run the tests before their inputs exist.
- template: /eng/pipelines/common/templates/global-build-step.yml
parameters:
- buildArgs: -s clr.r2rtests -os ios -arch arm64 -rf CoreCLR -lc Release -c $(_BuildConfig) -test
+ buildArgs: -s clr.r2rtests -rf CoreCLR -lc Release -c $(_BuildConfig) -test
displayName: Build and run R2R tests
condition: >-
or(
@@ -1011,7 +1024,7 @@ extends:
- build_windows_x64_checked_CoreCLR_ReleaseLibraries
timeoutInMinutes: 120
nameSuffix: CLR_R2R_Tests_windows_x64
- buildArgs: -s clr.r2rtests -os windows -arch x64 -lc Release -c $(_BuildConfig) -test
+ buildArgs: -s clr.r2rtests -lc Release -c $(_BuildConfig) -test
enablePublishTestResults: true
testResultsFormat: 'xunit'
preBuildSteps:
From c8fafdbc5dd08cc636689e91cf6d88592e73e6b4 Mon Sep 17 00:00:00 2001
From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com>
Date: Thu, 30 Jul 2026 13:05:53 -0700
Subject: [PATCH 16/17] Use OutputPath for copying crossgen2_inbuild output
items
---
.../ILCompiler.ReadyToRun.Tests.csproj | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj
index 61f9f40b11f8c0..5206ca6dc93bbe 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj
@@ -54,7 +54,7 @@
-
From a1ce566335e97a56328d7195095a09bedf515d49 Mon Sep 17 00:00:00 2001
From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com>
Date: Fri, 31 Jul 2026 11:55:51 -0700
Subject: [PATCH 17/17] Use OutputPath filename for crossgen2 TargetPath
---
.../ILCompiler.ReadyToRun.Tests.csproj | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj
index 5206ca6dc93bbe..1c77956d8bbe6b 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj
@@ -54,9 +54,10 @@
+
+ TargetPath="crossgen2/$([System.IO.Path]::GetFileName('%(Crossgen2InbuildOutputItems.OutputPath)'))" />