diff --git a/src/BenchmarkDotNet.Annotations/Jobs/RuntimeMoniker.cs b/src/BenchmarkDotNet.Annotations/Jobs/RuntimeMoniker.cs
index 81a2431629..ccca5840e9 100644
--- a/src/BenchmarkDotNet.Annotations/Jobs/RuntimeMoniker.cs
+++ b/src/BenchmarkDotNet.Annotations/Jobs/RuntimeMoniker.cs
@@ -248,5 +248,15 @@ public enum RuntimeMoniker
/// .NET 11 using MonoVM (not CLR which is the default)
///
Mono11_0,
+
+ ///
+ /// .NET 10 CLR with composite ReadyToRun compilation
+ ///
+ CompositeR2R10_0,
+
+ ///
+ /// .NET 11 CLR with composite ReadyToRun compilation
+ ///
+ CompositeR2R11_0,
}
}
diff --git a/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoser.cs b/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoser.cs
index 8430b907b6..1025c4e431 100644
--- a/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoser.cs
+++ b/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoser.cs
@@ -83,6 +83,8 @@ internal override bool IsSupported(RuntimeMoniker runtimeMoniker)
case RuntimeMoniker.Net90:
case RuntimeMoniker.Net10_0:
case RuntimeMoniker.Net11_0:
+ case RuntimeMoniker.CompositeR2R10_0:
+ case RuntimeMoniker.CompositeR2R11_0:
return true;
case RuntimeMoniker.NotRecognized:
case RuntimeMoniker.Mono:
diff --git a/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceDiagnoser.cs b/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceDiagnoser.cs
index bea8894868..6b30646998 100644
--- a/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceDiagnoser.cs
+++ b/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceDiagnoser.cs
@@ -86,6 +86,8 @@ internal override bool IsSupported(RuntimeMoniker runtimeMoniker)
case RuntimeMoniker.Net90:
case RuntimeMoniker.Net10_0:
case RuntimeMoniker.Net11_0:
+ case RuntimeMoniker.CompositeR2R10_0:
+ case RuntimeMoniker.CompositeR2R11_0:
return true;
case RuntimeMoniker.NotRecognized:
case RuntimeMoniker.Mono:
diff --git a/src/BenchmarkDotNet/ConsoleArguments/ConfigParser.cs b/src/BenchmarkDotNet/ConsoleArguments/ConfigParser.cs
index b8a3866ffe..86b1d7046d 100644
--- a/src/BenchmarkDotNet/ConsoleArguments/ConfigParser.cs
+++ b/src/BenchmarkDotNet/ConsoleArguments/ConfigParser.cs
@@ -18,6 +18,7 @@
using BenchmarkDotNet.Loggers;
using BenchmarkDotNet.Portability;
using BenchmarkDotNet.Reports;
+using BenchmarkDotNet.Toolchains.CompositeR2R;
using BenchmarkDotNet.Toolchains.CoreRun;
using BenchmarkDotNet.Toolchains.CsProj;
using BenchmarkDotNet.Toolchains.DotNetCli;
@@ -641,6 +642,10 @@ private static Job CreateJobForGivenRuntime(Job baseJob, string runtimeId, Comma
case RuntimeMoniker.Mono11_0:
return MakeMonoJob(baseJob, options, MonoRuntime.Mono11_0);
+ case RuntimeMoniker.CompositeR2R10_0:
+ case RuntimeMoniker.CompositeR2R11_0:
+ return CreateCompositeR2RJob(baseJob, options, runtimeMoniker.GetRuntime());
+
default:
throw new NotSupportedException($"Runtime {runtimeId} is not supported");
}
@@ -703,6 +708,21 @@ private static Job MakeMonoAOTLLVMJob(Job baseJob, CommandLineOptions options, s
return baseJob.WithRuntime(monoAotLLVMRuntime).WithToolchain(toolChain).WithId(monoAotLLVMRuntime.Name);
}
+ private static Job CreateCompositeR2RJob(Job baseJob, CommandLineOptions options, Runtime runtime)
+ {
+ var toolChain = CompositeR2RToolchain.From(
+ new NetCoreAppSettings(
+ targetFrameworkMoniker: runtime.MsBuildMoniker,
+ runtimeFrameworkVersion: null,
+ name: $"CompositeR2R {runtime.Name}",
+ customDotNetCliPath: options.CliPath?.FullName,
+ packagesPath: options.RestorePath?.FullName,
+ customRuntimePack: options.CustomRuntimePack,
+ aotCompilerPath: options.AOTCompilerPath != null ? options.AOTCompilerPath.ToString() : null));
+
+ return baseJob.WithRuntime(runtime).WithToolchain(toolChain).WithId("CompositeR2R");
+ }
+
private static Job MakeWasmJob(Job baseJob, CommandLineOptions options, string msBuildMoniker, RuntimeMoniker moniker)
{
bool wasmAot = options.AOTCompilerMode == MonoAotCompilerMode.wasm;
@@ -827,4 +847,4 @@ internal static bool TryParse(string runtime, out RuntimeMoniker runtimeMoniker)
return Enum.TryParse(runtime.Replace('.', '_'), ignoreCase: true, out runtimeMoniker);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/BenchmarkDotNet/Extensions/RuntimeMonikerExtensions.cs b/src/BenchmarkDotNet/Extensions/RuntimeMonikerExtensions.cs
index 1dad83d690..e3a3847695 100644
--- a/src/BenchmarkDotNet/Extensions/RuntimeMonikerExtensions.cs
+++ b/src/BenchmarkDotNet/Extensions/RuntimeMonikerExtensions.cs
@@ -74,6 +74,10 @@ internal static Runtime GetRuntime(this RuntimeMoniker runtimeMoniker)
return MonoRuntime.Mono10_0;
case RuntimeMoniker.Mono11_0:
return MonoRuntime.Mono11_0;
+ case RuntimeMoniker.CompositeR2R10_0:
+ return CoreRuntime.Core10_0;
+ case RuntimeMoniker.CompositeR2R11_0:
+ return CoreRuntime.Core11_0;
default:
throw new ArgumentOutOfRangeException(nameof(runtimeMoniker), runtimeMoniker, "Runtime Moniker not supported");
}
diff --git a/src/BenchmarkDotNet/Templates/CompositeR2RCsProj.txt b/src/BenchmarkDotNet/Templates/CompositeR2RCsProj.txt
new file mode 100644
index 0000000000..7a2126c940
--- /dev/null
+++ b/src/BenchmarkDotNet/Templates/CompositeR2RCsProj.txt
@@ -0,0 +1,69 @@
+
+
+
+
+ false
+ false
+ $PROGRAMNAME$
+ $TFM$
+ true
+ $PLATFORM$
+ $PROGRAMNAME$
+ Exe
+ False
+
+ false
+
+ false
+ false
+
+ false
+ true
+
+ true
+
+ latest
+
+ false
+
+ true
+ BenchmarkDotNet.Autogenerated.UniqueProgramName
+
+
+
+ true
+ $RUNTIMEIDENTIFIER$
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $COPIEDSETTINGS$
+
+
+
+
+
+
+
+ $RUNTIMEPACK$
+
+
+ $CROSSGEN2PACK$
+
+
+
+
diff --git a/src/BenchmarkDotNet/Toolchains/CompositeR2R/CompositeR2RGenerator.cs b/src/BenchmarkDotNet/Toolchains/CompositeR2R/CompositeR2RGenerator.cs
new file mode 100644
index 0000000000..d12fa79253
--- /dev/null
+++ b/src/BenchmarkDotNet/Toolchains/CompositeR2R/CompositeR2RGenerator.cs
@@ -0,0 +1,62 @@
+using System.IO;
+using System.Text;
+using System.Xml;
+using BenchmarkDotNet.Detectors;
+using BenchmarkDotNet.Extensions;
+using BenchmarkDotNet.Helpers;
+using BenchmarkDotNet.Loggers;
+using BenchmarkDotNet.Running;
+using BenchmarkDotNet.Toolchains.CsProj;
+using BenchmarkDotNet.Toolchains.DotNetCli;
+
+namespace BenchmarkDotNet.Toolchains.CompositeR2R
+{
+ public class CompositeR2RGenerator : CsProjGenerator
+ {
+ private readonly string CustomRuntimePack;
+ private readonly string Crossgen2Pack;
+
+ public CompositeR2RGenerator(string targetFrameworkMoniker, string cliPath, string packagesPath, string customRuntimePack, string crossgen2Pack)
+ : base(targetFrameworkMoniker, cliPath, packagesPath, runtimeFrameworkVersion: null)
+ {
+ CustomRuntimePack = customRuntimePack;
+ Crossgen2Pack = crossgen2Pack;
+ BenchmarkRunCallType = Code.CodeGenBenchmarkRunCallType.Direct;
+ }
+
+ protected override void GenerateProject(BuildPartition buildPartition, ArtifactsPaths artifactsPaths, ILogger logger)
+ {
+ BenchmarkCase benchmark = buildPartition.RepresentativeBenchmarkCase;
+ var projectFile = GetProjectFilePath(benchmark.Descriptor.Type, logger);
+
+ var xmlDoc = new XmlDocument();
+ xmlDoc.Load(projectFile.FullName);
+ var (customProperties, sdkName) = GetSettingsThatNeedToBeCopied(xmlDoc, projectFile);
+
+ string content = new StringBuilder(ResourceHelper.LoadTemplate("CompositeR2RCsProj.txt"))
+ .Replace("$PLATFORM$", buildPartition.Platform.ToConfig())
+ .Replace("$CODEFILENAME$", Path.GetFileName(artifactsPaths.ProgramCodePath))
+ .Replace("$CSPROJPATH$", projectFile.FullName)
+ .Replace("$TFM$", TargetFrameworkMoniker)
+ .Replace("$PROGRAMNAME$", artifactsPaths.ProgramName)
+ .Replace("$COPIEDSETTINGS$", customProperties)
+ .Replace("$SDKNAME$", sdkName)
+ .Replace("$RUNTIMEPACK$", CustomRuntimePack)
+ .Replace("$CROSSGEN2PACK$", Crossgen2Pack)
+ .Replace("$RUNTIMEIDENTIFIER$", CustomDotNetCliToolchainBuilder.GetPortableRuntimeIdentifier())
+ .ToString();
+
+ File.WriteAllText(artifactsPaths.ProjectFilePath, content);
+
+ GatherReferences(buildPartition, artifactsPaths, logger);
+ }
+
+ protected override string GetExecutablePath(string binariesDirectoryPath, string programName)
+ => OsDetector.IsWindows()
+ ? Path.Combine(binariesDirectoryPath, $"{programName}.exe")
+ : Path.Combine(binariesDirectoryPath, programName);
+
+ protected override string GetBinariesDirectoryPath(string buildArtifactsDirectoryPath, string configuration)
+ => Path.Combine(buildArtifactsDirectoryPath, "bin", configuration, TargetFrameworkMoniker, CustomDotNetCliToolchainBuilder.GetPortableRuntimeIdentifier(), "publish");
+ }
+}
diff --git a/src/BenchmarkDotNet/Toolchains/CompositeR2R/CompositeR2RToolchain.cs b/src/BenchmarkDotNet/Toolchains/CompositeR2R/CompositeR2RToolchain.cs
new file mode 100644
index 0000000000..33f02c882c
--- /dev/null
+++ b/src/BenchmarkDotNet/Toolchains/CompositeR2R/CompositeR2RToolchain.cs
@@ -0,0 +1,44 @@
+using BenchmarkDotNet.Characteristics;
+using BenchmarkDotNet.Running;
+using BenchmarkDotNet.Toolchains.CsProj;
+using BenchmarkDotNet.Toolchains.DotNetCli;
+using BenchmarkDotNet.Validators;
+using JetBrains.Annotations;
+using System;
+using System.Collections.Generic;
+
+namespace BenchmarkDotNet.Toolchains.CompositeR2R
+{
+ [PublicAPI]
+ public class CompositeR2RToolchain : CsProjCoreToolchain, IEquatable
+ {
+ private readonly string _customDotNetCliPath;
+ private CompositeR2RToolchain(string name, IGenerator generator, IBuilder builder, IExecutor executor, string customDotNetCliPath)
+ : base(name, generator, builder, executor, customDotNetCliPath)
+ {
+ _customDotNetCliPath = customDotNetCliPath;
+ }
+
+ [PublicAPI]
+ public static new IToolchain From(NetCoreAppSettings settings)
+ => new CompositeR2RToolchain(settings.Name,
+ new CompositeR2RGenerator(settings.TargetFrameworkMoniker, settings.CustomDotNetCliPath, settings.PackagesPath, settings.CustomRuntimePack, settings.AOTCompilerPath),
+ new DotNetCliPublisher(settings.TargetFrameworkMoniker, settings.CustomDotNetCliPath),
+ new Executor(),
+ settings.CustomDotNetCliPath);
+
+ public override IEnumerable Validate(BenchmarkCase benchmarkCase, IResolver resolver)
+ {
+ foreach (var validationError in DotNetSdkValidator.ValidateCoreSdks(_customDotNetCliPath, benchmarkCase))
+ {
+ yield return validationError;
+ }
+ }
+
+ public override bool Equals(object obj) => obj is CompositeR2RToolchain typed && Equals(typed);
+
+ public bool Equals(CompositeR2RToolchain other) => Generator.Equals(other.Generator);
+
+ public override int GetHashCode() => Generator.GetHashCode();
+ }
+}