diff --git a/BenchmarkDotNet.sln b/BenchmarkDotNet.sln
index 1df6c0aabd..186fecb04b 100644
--- a/BenchmarkDotNet.sln
+++ b/BenchmarkDotNet.sln
@@ -59,6 +59,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BenchmarkDotNet.Exporters.P
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BenchmarkDotNet.Exporters.Plotting.Tests", "tests\BenchmarkDotNet.Exporters.Plotting.Tests\BenchmarkDotNet.Exporters.Plotting.Tests.csproj", "{199AC83E-30BD-40CD-87CE-0C838AC0320D}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BenchmarkDotNet.Weaver", "src\BenchmarkDotNet.Weaver\BenchmarkDotNet.Weaver.csproj", "{5731DE42-16FE-430E-BA90-0EBE714CB221}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -161,6 +163,10 @@ Global
{199AC83E-30BD-40CD-87CE-0C838AC0320D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{199AC83E-30BD-40CD-87CE-0C838AC0320D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{199AC83E-30BD-40CD-87CE-0C838AC0320D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5731DE42-16FE-430E-BA90-0EBE714CB221}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5731DE42-16FE-430E-BA90-0EBE714CB221}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5731DE42-16FE-430E-BA90-0EBE714CB221}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5731DE42-16FE-430E-BA90-0EBE714CB221}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -190,6 +196,7 @@ Global
{2E2283A3-6DA6-4482-8518-99D6D9F689AB} = {D6597E3A-6892-4A68-8E14-042FC941FDA2}
{B92ECCEF-7C27-4012-9E19-679F3C40A6A6} = {D6597E3A-6892-4A68-8E14-042FC941FDA2}
{199AC83E-30BD-40CD-87CE-0C838AC0320D} = {14195214-591A-45B7-851A-19D3BA2413F9}
+ {5731DE42-16FE-430E-BA90-0EBE714CB221} = {D6597E3A-6892-4A68-8E14-042FC941FDA2}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {4D9AF12B-1F7F-45A7-9E8C-E4E46ADCBD1F}
diff --git a/NuGet.Config b/NuGet.Config
index 7507704b8b..ca58921f13 100644
--- a/NuGet.Config
+++ b/NuGet.Config
@@ -8,11 +8,13 @@
-
+
+
+
diff --git a/build/BenchmarkDotNet.Build/Program.cs b/build/BenchmarkDotNet.Build/Program.cs
index 51dd875fbd..a0886b6ba6 100644
--- a/build/BenchmarkDotNet.Build/Program.cs
+++ b/build/BenchmarkDotNet.Build/Program.cs
@@ -16,8 +16,29 @@ public static int Main(string[] args)
}
}
+[TaskName(Name)]
+[TaskDescription("Pack Weaver")]
+public class PackWeaverTask : FrostingTask, IHelpProvider
+{
+ private const string Name = "pack-weaver";
+
+ public override void Run(BuildContext context) => context.BuildRunner.PackWeaver();
+
+ public HelpInfo GetHelp()
+ {
+ return new HelpInfo
+ {
+ Examples = new[]
+ {
+ new Example(Name)
+ }
+ };
+ }
+}
+
[TaskName(Name)]
[TaskDescription("Restore NuGet packages")]
+[IsDependentOn(typeof(PackWeaverTask))]
public class RestoreTask : FrostingTask, IHelpProvider
{
private const string Name = "restore";
diff --git a/build/BenchmarkDotNet.Build/Runners/BuildRunner.cs b/build/BenchmarkDotNet.Build/Runners/BuildRunner.cs
index a38ce7e79d..4569110a2c 100644
--- a/build/BenchmarkDotNet.Build/Runners/BuildRunner.cs
+++ b/build/BenchmarkDotNet.Build/Runners/BuildRunner.cs
@@ -8,6 +8,7 @@
using Cake.Common.Tools.DotNet.Workload.Install;
using Cake.Core;
using Cake.Core.IO;
+using System.Linq;
namespace BenchmarkDotNet.Build.Runners;
@@ -20,6 +21,34 @@ public BuildRunner(BuildContext context)
this.context = context;
}
+ public void PackWeaver()
+ {
+ var weaverPath = context.AllPackableSrcProjects.Single(p => p.GetFilename() == "BenchmarkDotNet.Weaver.csproj");
+
+ context.DotNetRestore(weaverPath.GetDirectory().FullPath,
+ new DotNetRestoreSettings
+ {
+ MSBuildSettings = context.MsBuildSettingsRestore
+ });
+
+ context.Information("BuildSystemProvider: " + context.BuildSystem().Provider);
+ context.DotNetBuild(weaverPath.FullPath, new DotNetBuildSettings
+ {
+ NoRestore = true,
+ DiagnosticOutput = true,
+ MSBuildSettings = context.MsBuildSettingsBuild,
+ Configuration = context.BuildConfiguration,
+ Verbosity = context.BuildVerbosity
+ });
+
+ context.DotNetPack(weaverPath.FullPath, new DotNetPackSettings
+ {
+ OutputDirectory = weaverPath.GetDirectory().Combine("packages"),
+ MSBuildSettings = context.MsBuildSettingsPack,
+ Configuration = context.BuildConfiguration
+ });
+ }
+
public void Restore()
{
context.DotNetRestore(context.SolutionFile.FullPath,
@@ -71,7 +100,7 @@ public void Pack()
var settingsSrc = new DotNetPackSettings
{
OutputDirectory = context.ArtifactsDirectory,
- ArgumentCustomization = args => args.Append("--include-symbols").Append("-p:SymbolPackageFormat=snupkg"),
+ ArgumentCustomization = args => args.Append("--include-symbols").Append("-p:SymbolPackageFormat=snupkg").Append("-p:IsFullPack=true"),
MSBuildSettings = context.MsBuildSettingsPack,
Configuration = context.BuildConfiguration
};
diff --git a/build/common.props b/build/common.props
index 972f3ea9f6..c1498bc380 100644
--- a/build/common.props
+++ b/build/common.props
@@ -90,4 +90,9 @@
+
+
+
+ -1
+
diff --git a/samples/BenchmarkDotNet.Samples.FSharp/BenchmarkDotNet.Samples.FSharp.fsproj b/samples/BenchmarkDotNet.Samples.FSharp/BenchmarkDotNet.Samples.FSharp.fsproj
index 457b660115..368a9f6162 100644
--- a/samples/BenchmarkDotNet.Samples.FSharp/BenchmarkDotNet.Samples.FSharp.fsproj
+++ b/samples/BenchmarkDotNet.Samples.FSharp/BenchmarkDotNet.Samples.FSharp.fsproj
@@ -3,6 +3,7 @@
true
+
Exe
net462;net8.0
diff --git a/samples/BenchmarkDotNet.Samples/IntroSmokeStringBuilder.cs b/samples/BenchmarkDotNet.Samples/IntroSmokeStringBuilder.cs
new file mode 100644
index 0000000000..65b9355095
--- /dev/null
+++ b/samples/BenchmarkDotNet.Samples/IntroSmokeStringBuilder.cs
@@ -0,0 +1,34 @@
+using BenchmarkDotNet.Attributes;
+using System.Text;
+
+namespace BenchmarkDotNet.Samples
+{
+ [MemoryDiagnoser(false)]
+ public class IntroSmokeStringBuilder
+ {
+ [Benchmark]
+ [Arguments(1)]
+ [Arguments(1_000)]
+ public StringBuilder Append_Strings(int repeat)
+ {
+ StringBuilder builder = new StringBuilder();
+
+ // strings are not sorted by length to mimic real input
+ for (int i = 0; i < repeat; i++)
+ {
+ builder.Append("12345");
+ builder.Append("1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMN");
+ builder.Append("1234567890abcdefghijklmnopqrstuvwxy");
+ builder.Append("1234567890");
+ builder.Append("1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHI");
+ builder.Append("1234567890abcde");
+ builder.Append("1234567890abcdefghijklmnopqrstuvwxyzABCD");
+ builder.Append("1234567890abcdefghijklmnopqrst");
+ builder.Append("1234567890abcdefghij");
+ builder.Append("1234567890abcdefghijklmno");
+ }
+
+ return builder;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/BenchmarkDotNet.Annotations/BenchmarkDotNet.Annotations.csproj b/src/BenchmarkDotNet.Annotations/BenchmarkDotNet.Annotations.csproj
index ccc51bcd9a..c28950ab9a 100644
--- a/src/BenchmarkDotNet.Annotations/BenchmarkDotNet.Annotations.csproj
+++ b/src/BenchmarkDotNet.Annotations/BenchmarkDotNet.Annotations.csproj
@@ -14,4 +14,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/BenchmarkDotNet.Weaver/.gitignore b/src/BenchmarkDotNet.Weaver/.gitignore
new file mode 100644
index 0000000000..ddb9a15db5
--- /dev/null
+++ b/src/BenchmarkDotNet.Weaver/.gitignore
@@ -0,0 +1 @@
+!packages/
\ No newline at end of file
diff --git a/src/BenchmarkDotNet.Weaver/BenchmarkDotNet.Weaver.csproj b/src/BenchmarkDotNet.Weaver/BenchmarkDotNet.Weaver.csproj
new file mode 100644
index 0000000000..00c5f2f1c5
--- /dev/null
+++ b/src/BenchmarkDotNet.Weaver/BenchmarkDotNet.Weaver.csproj
@@ -0,0 +1,27 @@
+
+
+
+
+
+ netstandard2.0
+ $(VersionSuffix)$(WeaverVersionSuffix)
+ $(MSBuildThisFileDirectory)bin\$(Configuration)
+ true
+ false
+ $(NoWarn);NU5100;NU5128
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/BenchmarkDotNet.Weaver/buildTransitive/netstandard2.0/BenchmarkDotNet.Weaver.targets b/src/BenchmarkDotNet.Weaver/buildTransitive/netstandard2.0/BenchmarkDotNet.Weaver.targets
new file mode 100644
index 0000000000..79c1aaa2a5
--- /dev/null
+++ b/src/BenchmarkDotNet.Weaver/buildTransitive/netstandard2.0/BenchmarkDotNet.Weaver.targets
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/BenchmarkDotNet.Weaver/packages/BenchmarkDotNet.Weaver.0.14.1-develop-1.nupkg b/src/BenchmarkDotNet.Weaver/packages/BenchmarkDotNet.Weaver.0.14.1-develop-1.nupkg
new file mode 100644
index 0000000000..f652b4bca6
Binary files /dev/null and b/src/BenchmarkDotNet.Weaver/packages/BenchmarkDotNet.Weaver.0.14.1-develop-1.nupkg differ
diff --git a/src/BenchmarkDotNet.Weaver/src/WeaveAssemblyTask.cs b/src/BenchmarkDotNet.Weaver/src/WeaveAssemblyTask.cs
new file mode 100644
index 0000000000..765faff0f1
--- /dev/null
+++ b/src/BenchmarkDotNet.Weaver/src/WeaveAssemblyTask.cs
@@ -0,0 +1,122 @@
+using System;
+using System.IO;
+using System.Linq;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+using Mono.Cecil;
+
+namespace BenchmarkDotNet.Weaver;
+
+internal class CustomAssemblyResolver : DefaultAssemblyResolver
+{
+ public override AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters parameters)
+ // NetStandard causes StackOverflow. https://github.com/jbevain/cecil/issues/573
+ // Mscorlib fails to resolve in Visual Studio. https://github.com/jbevain/cecil/issues/966
+ // We don't care about any types from runtime assemblies anyway, so just skip resolving them.
+ => name.Name is "netstandard" or "mscorlib" or "System.Runtime" or "System.Private.CoreLib"
+ ? null
+ : base.Resolve(name, parameters);
+}
+
+///
+/// The Task used by MSBuild to weave the assembly.
+///
+public sealed class WeaveAssemblyTask : Task
+{
+ ///
+ /// The directory of the output.
+ ///
+ [Required]
+ public string TargetDir { get; set; }
+
+ ///
+ /// The path of the target assembly.
+ ///
+ [Required]
+ public string TargetAssembly { get; set; }
+
+ ///
+ /// Runs the weave assembly task.
+ ///
+ /// if successful; otherwise.
+ public override bool Execute()
+ {
+ if (!File.Exists(TargetAssembly))
+ {
+ Log.LogError($"Assembly not found: {TargetAssembly}");
+ return false;
+ }
+
+ var resolver = new CustomAssemblyResolver();
+ resolver.AddSearchDirectory(TargetDir);
+
+ // ReaderParameters { ReadWrite = true } is necessary to later write the file.
+ // https://stackoverflow.com/questions/41840455/locked-target-assembly-with-mono-cecil-and-pcl-code-injection
+ var readerParameters = new ReaderParameters
+ {
+ ReadWrite = true,
+ AssemblyResolver = resolver
+ };
+
+ bool benchmarkMethodsImplAdjusted = false;
+ try
+ {
+ using var module = ModuleDefinition.ReadModule(TargetAssembly, readerParameters);
+
+ foreach (var type in module.Types)
+ {
+ ProcessType(type, ref benchmarkMethodsImplAdjusted);
+ }
+
+ // Write the modified assembly to file.
+ module.Write();
+ }
+ catch (Exception e)
+ {
+ if (benchmarkMethodsImplAdjusted)
+ {
+ Log.LogWarning($"Benchmark methods were found that require NoInlining, and assembly weaving failed.{Environment.NewLine}{e}");
+ }
+ }
+ return true;
+ }
+
+ private static void ProcessType(TypeDefinition type, ref bool benchmarkMethodsImplAdjusted)
+ {
+ // We can skip non-public types as they are not valid for benchmarks.
+ if (type.IsNotPublic)
+ {
+ return;
+ }
+
+ // Remove AggressiveInlining and add NoInlining to all [Benchmark] methods.
+ foreach (var method in type.Methods)
+ {
+ if (method.CustomAttributes.Any(IsBenchmarkAttribute))
+ {
+ var oldImpl = method.ImplAttributes;
+ method.ImplAttributes = (oldImpl & ~MethodImplAttributes.AggressiveInlining) | MethodImplAttributes.NoInlining;
+ benchmarkMethodsImplAdjusted |= (oldImpl & MethodImplAttributes.NoInlining) == 0;
+ }
+ }
+
+ // Recursively process nested types
+ foreach (var nestedType in type.NestedTypes)
+ {
+ ProcessType(nestedType, ref benchmarkMethodsImplAdjusted);
+ }
+ }
+
+ private static bool IsBenchmarkAttribute(CustomAttribute attribute)
+ {
+ // BenchmarkAttribute is unsealed, so we need to walk its hierarchy.
+ for (var attr = attribute.AttributeType; attr != null; attr = attr.Resolve()?.BaseType)
+ {
+ if (attr.FullName == "BenchmarkDotNet.Attributes.BenchmarkAttribute")
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/src/BenchmarkDotNet/Code/CodeGenerator.cs b/src/BenchmarkDotNet/Code/CodeGenerator.cs
index 005564b77c..bd5705c6c3 100644
--- a/src/BenchmarkDotNet/Code/CodeGenerator.cs
+++ b/src/BenchmarkDotNet/Code/CodeGenerator.cs
@@ -37,24 +37,16 @@ internal static string Generate(BuildPartition buildPartition)
string passArguments = GetPassArguments(benchmark);
- string compilationId = $"{provider.ReturnsDefinition}_{buildInfo.Id}";
-
AddNonEmptyUnique(additionalLogic, benchmark.Descriptor.AdditionalLogic);
string benchmarkTypeCode = new SmartStringBuilder(ResourceHelper.LoadTemplate("BenchmarkType.txt"))
.Replace("$ID$", buildInfo.Id.ToString())
.Replace("$OperationsPerInvoke$", provider.OperationsPerInvoke)
.Replace("$WorkloadTypeName$", provider.WorkloadTypeName)
- .Replace("$WorkloadMethodDelegate$", provider.WorkloadMethodDelegate(passArguments))
- .Replace("$WorkloadMethodReturnType$", provider.WorkloadMethodReturnTypeName)
- .Replace("$WorkloadMethodReturnTypeModifiers$", provider.WorkloadMethodReturnTypeModifiers)
- .Replace("$OverheadMethodReturnTypeName$", provider.OverheadMethodReturnTypeName)
.Replace("$GlobalSetupMethodName$", provider.GlobalSetupMethodName)
.Replace("$GlobalCleanupMethodName$", provider.GlobalCleanupMethodName)
.Replace("$IterationSetupMethodName$", provider.IterationSetupMethodName)
.Replace("$IterationCleanupMethodName$", provider.IterationCleanupMethodName)
- .Replace("$OverheadImplementation$", provider.OverheadImplementation)
- .Replace("$ConsumeField$", provider.ConsumeField)
.Replace("$JobSetDefinition$", GetJobsSetDefinition(benchmark))
.Replace("$ParamsContent$", GetParamsContent(benchmark))
.Replace("$ArgumentsDefinition$", GetArgumentsDefinition(benchmark))
@@ -65,7 +57,7 @@ internal static string Generate(BuildPartition buildPartition)
.Replace("$MeasureExtraStats$", buildInfo.Config.HasExtraStatsDiagnoser() ? "true" : "false")
.Replace("$DisassemblerEntryMethodName$", DisassemblerConstants.DisassemblerEntryMethodName)
.Replace("$WorkloadMethodCall$", provider.GetWorkloadMethodCall(passArguments))
- .RemoveRedundantIfDefines(compilationId);
+ .ToString();
benchmarkTypeCode = Unroll(benchmarkTypeCode, benchmark.Job.ResolveValue(RunMode.UnrollFactorCharacteristic, EnvironmentResolver.Instance));
@@ -154,36 +146,21 @@ private static DeclarationsProvider GetDeclarationsProvider(Descriptor descripto
if (method.ReturnType == typeof(Task) || method.ReturnType == typeof(ValueTask))
{
- return new TaskDeclarationsProvider(descriptor);
+ return new AsyncDeclarationsProvider(descriptor);
}
if (method.ReturnType.GetTypeInfo().IsGenericType
&& (method.ReturnType.GetTypeInfo().GetGenericTypeDefinition() == typeof(Task<>)
|| method.ReturnType.GetTypeInfo().GetGenericTypeDefinition() == typeof(ValueTask<>)))
{
- return new GenericTaskDeclarationsProvider(descriptor);
- }
-
- if (method.ReturnType == typeof(void))
- {
- bool isUsingAsyncKeyword = method.HasAttribute();
- if (isUsingAsyncKeyword)
- {
- throw new NotSupportedException("async void is not supported by design");
- }
-
- return new VoidDeclarationsProvider(descriptor);
+ return new AsyncDeclarationsProvider(descriptor);
}
- if (method.ReturnType.IsByRef)
+ if (method.ReturnType == typeof(void) && method.HasAttribute())
{
- // System.Runtime.CompilerServices.IsReadOnlyAttribute is part of .NET Standard 2.1, we can't use it here..
- if (method.ReturnParameter.GetCustomAttributes().Any(attribute => attribute.GetType().Name == "IsReadOnlyAttribute"))
- return new ByReadOnlyRefDeclarationsProvider(descriptor);
- else
- return new ByRefDeclarationsProvider(descriptor);
+ throw new NotSupportedException("async void is not supported by design");
}
- return new NonVoidDeclarationsProvider(descriptor);
+ return new SyncDeclarationsProvider(descriptor);
}
// internal for tests
@@ -305,31 +282,6 @@ public SmartStringBuilder Replace(string oldValue, string? newValue)
return this;
}
- public string RemoveRedundantIfDefines(string id)
- {
- var oldLines = builder.ToString().Split('\n');
- var newLines = new List();
- bool keepAdding = true;
-
- foreach (string line in oldLines)
- {
- if (line.StartsWith("#if RETURNS") || line.StartsWith("#elif RETURNS"))
- {
- keepAdding = line.Contains(id);
- }
- else if (line.StartsWith("#endif // RETURNS"))
- {
- keepAdding = true;
- }
- else if (keepAdding)
- {
- newLines.Add(line);
- }
- }
-
- return string.Join("\n", newLines);
- }
-
public override string ToString() => builder.ToString();
}
}
diff --git a/src/BenchmarkDotNet/Code/DeclarationsProvider.cs b/src/BenchmarkDotNet/Code/DeclarationsProvider.cs
index 7528e8ed62..0c9a8dce46 100644
--- a/src/BenchmarkDotNet/Code/DeclarationsProvider.cs
+++ b/src/BenchmarkDotNet/Code/DeclarationsProvider.cs
@@ -1,10 +1,6 @@
-using System;
-using System.Linq;
-using System.Reflection;
+using System.Reflection;
using System.Threading.Tasks;
-using BenchmarkDotNet.Engines;
using BenchmarkDotNet.Extensions;
-using BenchmarkDotNet.Helpers;
using BenchmarkDotNet.Running;
namespace BenchmarkDotNet.Code
@@ -30,26 +26,8 @@ internal abstract class DeclarationsProvider
public string IterationCleanupMethodName => Descriptor.IterationCleanupMethod?.Name ?? EmptyAction;
- public abstract string ReturnsDefinition { get; }
-
- protected virtual Type WorkloadMethodReturnType => Descriptor.WorkloadMethod.ReturnType;
-
- public virtual string WorkloadMethodReturnTypeName => WorkloadMethodReturnType.GetCorrectCSharpTypeName();
-
- public virtual string WorkloadMethodDelegate(string passArguments) => Descriptor.WorkloadMethod.Name;
-
- public virtual string WorkloadMethodReturnTypeModifiers => null;
-
public virtual string GetWorkloadMethodCall(string passArguments) => $"{Descriptor.WorkloadMethod.Name}({passArguments})";
- public virtual string ConsumeField => null;
-
- protected abstract Type OverheadMethodReturnType { get; }
-
- public string OverheadMethodReturnTypeName => OverheadMethodReturnType.GetCorrectCSharpTypeName();
-
- public abstract string OverheadImplementation { get; }
-
private string GetMethodName(MethodInfo method)
{
if (method == null)
@@ -70,104 +48,14 @@ private string GetMethodName(MethodInfo method)
}
}
- internal class VoidDeclarationsProvider : DeclarationsProvider
- {
- public VoidDeclarationsProvider(Descriptor descriptor) : base(descriptor) { }
-
- public override string ReturnsDefinition => "RETURNS_VOID";
-
- protected override Type OverheadMethodReturnType => typeof(void);
-
- public override string OverheadImplementation => string.Empty;
- }
-
- internal class NonVoidDeclarationsProvider : DeclarationsProvider
- {
- public NonVoidDeclarationsProvider(Descriptor descriptor) : base(descriptor) { }
-
- public override string ConsumeField
- => !Consumer.IsConsumable(WorkloadMethodReturnType) && Consumer.HasConsumableField(WorkloadMethodReturnType, out var field)
- ? $".{field.Name}"
- : null;
-
- protected override Type OverheadMethodReturnType
- => Consumer.IsConsumable(WorkloadMethodReturnType)
- ? WorkloadMethodReturnType
- : (Consumer.HasConsumableField(WorkloadMethodReturnType, out var field)
- ? field.FieldType
- : typeof(int)); // we return this simple type because creating bigger ValueType could take longer than benchmarked method itself
-
- public override string OverheadImplementation
- {
- get
- {
- string value;
- var type = OverheadMethodReturnType;
- if (type.GetTypeInfo().IsPrimitive)
- value = $"default({type.GetCorrectCSharpTypeName()})";
- else if (type.GetTypeInfo().IsClass || type.GetTypeInfo().IsInterface)
- value = "null";
- else
- value = SourceCodeHelper.ToSourceCode(Activator.CreateInstance(type)) + ";";
- return $"return {value};";
- }
- }
-
- public override string ReturnsDefinition
- => Consumer.IsConsumable(WorkloadMethodReturnType) || Consumer.HasConsumableField(WorkloadMethodReturnType, out _)
- ? "RETURNS_CONSUMABLE"
- : "RETURNS_NON_CONSUMABLE_STRUCT";
- }
-
- internal class ByRefDeclarationsProvider : NonVoidDeclarationsProvider
+ internal class SyncDeclarationsProvider : DeclarationsProvider
{
- public ByRefDeclarationsProvider(Descriptor descriptor) : base(descriptor) { }
-
- protected override Type OverheadMethodReturnType => typeof(IntPtr);
-
- public override string WorkloadMethodReturnTypeName => base.WorkloadMethodReturnTypeName.Replace("&", string.Empty);
-
- public override string ConsumeField => null;
-
- public override string OverheadImplementation => $"return default(System.{nameof(IntPtr)});";
-
- public override string ReturnsDefinition => "RETURNS_BYREF";
-
- public override string WorkloadMethodReturnTypeModifiers => "ref";
+ public SyncDeclarationsProvider(Descriptor descriptor) : base(descriptor) { }
}
- internal class ByReadOnlyRefDeclarationsProvider : ByRefDeclarationsProvider
+ internal class AsyncDeclarationsProvider : DeclarationsProvider
{
- public ByReadOnlyRefDeclarationsProvider(Descriptor descriptor) : base(descriptor) { }
-
- public override string ReturnsDefinition => "RETURNS_BYREF_READONLY";
-
- public override string WorkloadMethodReturnTypeModifiers => "ref readonly";
- }
-
- internal class TaskDeclarationsProvider : VoidDeclarationsProvider
- {
- public TaskDeclarationsProvider(Descriptor descriptor) : base(descriptor) { }
-
- public override string WorkloadMethodDelegate(string passArguments)
- => $"({passArguments}) => {{ BenchmarkDotNet.Helpers.AwaitHelper.GetResult({Descriptor.WorkloadMethod.Name}({passArguments})); }}";
-
- public override string GetWorkloadMethodCall(string passArguments) => $"BenchmarkDotNet.Helpers.AwaitHelper.GetResult({Descriptor.WorkloadMethod.Name}({passArguments}))";
-
- protected override Type WorkloadMethodReturnType => typeof(void);
- }
-
- ///
- /// declarations provider for and
- ///
- internal class GenericTaskDeclarationsProvider : NonVoidDeclarationsProvider
- {
- public GenericTaskDeclarationsProvider(Descriptor descriptor) : base(descriptor) { }
-
- protected override Type WorkloadMethodReturnType => Descriptor.WorkloadMethod.ReturnType.GetTypeInfo().GetGenericArguments().Single();
-
- public override string WorkloadMethodDelegate(string passArguments)
- => $"({passArguments}) => {{ return BenchmarkDotNet.Helpers.AwaitHelper.GetResult({Descriptor.WorkloadMethod.Name}({passArguments})); }}";
+ public AsyncDeclarationsProvider(Descriptor descriptor) : base(descriptor) { }
public override string GetWorkloadMethodCall(string passArguments) => $"BenchmarkDotNet.Helpers.AwaitHelper.GetResult({Descriptor.WorkloadMethod.Name}({passArguments}))";
}
diff --git a/src/BenchmarkDotNet/Engines/Consumer.cs b/src/BenchmarkDotNet/Engines/Consumer.cs
index 55f8e3b040..5c687f20e6 100644
--- a/src/BenchmarkDotNet/Engines/Consumer.cs
+++ b/src/BenchmarkDotNet/Engines/Consumer.cs
@@ -1,7 +1,4 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading;
using JetBrains.Annotations;
@@ -11,13 +8,6 @@ namespace BenchmarkDotNet.Engines
{
public class Consumer
{
- private static readonly HashSet SupportedTypes
- = new HashSet(
- typeof(Consumer).GetTypeInfo()
- .DeclaredFields
- .Where(field => !field.IsStatic) // exclude this HashSet itself
- .Select(field => field.FieldType));
-
#pragma warning disable IDE0052 // Remove unread private members
private volatile byte byteHolder;
private volatile sbyte sbyteHolder;
@@ -153,28 +143,5 @@ public void Consume(in T value)
else
DeadCodeEliminationHelper.KeepAliveWithoutBoxingReadonly(value); // non-primitive and nullable value types
}
-
- internal static bool IsConsumable(Type type)
- => SupportedTypes.Contains(type) || type.GetTypeInfo().IsClass || type.GetTypeInfo().IsInterface;
-
- internal static bool HasConsumableField(Type type, out FieldInfo? consumableField)
- {
- var typeInfo = type.GetTypeInfo();
-
- if (typeInfo.IsEnum)
- {
- // Enums are tricky bastards which report "value__" field, which is public for reflection, but inaccessible via C#
- consumableField = null;
- return false;
- }
-
- var publicInstanceFields = typeInfo.DeclaredFields
- .Where(field => field.IsPublic && !field.IsStatic)
- .ToArray();
-
- consumableField = publicInstanceFields.FirstOrDefault(field => IsConsumable(field.FieldType));
-
- return consumableField != null;
- }
}
}
\ No newline at end of file
diff --git a/src/BenchmarkDotNet/Helpers/Reflection.Emit/IlGeneratorCallExtensions.cs b/src/BenchmarkDotNet/Helpers/Reflection.Emit/IlGeneratorCallExtensions.cs
index 6f9a794e01..214963dd78 100644
--- a/src/BenchmarkDotNet/Helpers/Reflection.Emit/IlGeneratorCallExtensions.cs
+++ b/src/BenchmarkDotNet/Helpers/Reflection.Emit/IlGeneratorCallExtensions.cs
@@ -7,25 +7,6 @@ namespace BenchmarkDotNet.Helpers.Reflection.Emit
{
internal static class IlGeneratorCallExtensions
{
- public static LocalBuilder DeclareOptionalLocalForInstanceCall(
- this ILGenerator ilBuilder,
- Type localType,
- MethodInfo methodToCall)
- {
- if (methodToCall.DeclaringType == null)
- throw new ArgumentException($"The {nameof(methodToCall)} should have non-null {nameof(methodToCall.DeclaringType)}.");
-
- if (methodToCall.IsStatic)
- return null;
-
- if (!methodToCall.DeclaringType.IsAssignableFrom(localType))
- throw new ArgumentException($"{methodToCall.DeclaringType} is not assignable from {localType}.");
-
- return localType.IsValueType && localType != typeof(void)
- ? ilBuilder.DeclareLocal(localType)
- : null;
- }
-
public static void EmitStaticCall(
this ILGenerator ilBuilder,
MethodInfo methodToCall,
diff --git a/src/BenchmarkDotNet/Helpers/Reflection.Emit/IlGeneratorDefaultValueExtensions.cs b/src/BenchmarkDotNet/Helpers/Reflection.Emit/IlGeneratorDefaultValueExtensions.cs
deleted file mode 100644
index 4b23db0141..0000000000
--- a/src/BenchmarkDotNet/Helpers/Reflection.Emit/IlGeneratorDefaultValueExtensions.cs
+++ /dev/null
@@ -1,182 +0,0 @@
-using System;
-using System.Reflection.Emit;
-
-namespace BenchmarkDotNet.Helpers.Reflection.Emit
-{
- internal static class IlGeneratorDefaultValueExtensions
- {
- public static LocalBuilder DeclareOptionalLocalForReturnDefault(this ILGenerator ilBuilder, Type resultType)
- {
- return resultType.UseInitObjForReturnDefault()
- ? ilBuilder.DeclareLocal(resultType)
- : null;
- }
-
- public static void EmitSetLocalToDefault(this ILGenerator ilBuilder, LocalBuilder local)
- {
- var resultType = local.LocalType;
- switch (resultType)
- {
- case Type t when t == typeof(void):
- break;
- case Type t when t.IsClass || t.IsInterface:
- ilBuilder.Emit(OpCodes.Ldnull);
- ilBuilder.EmitStloc(local);
- break;
- case Type t when t.UseInitObjForInitLocal():
- EmitInitObj(ilBuilder, resultType, local);
- break;
- default:
- EmitLoadDefaultPrimitive(ilBuilder, resultType);
- ilBuilder.EmitStloc(local);
- break;
- }
- }
-
- public static void EmitReturnDefault(this ILGenerator ilBuilder, Type resultType, LocalBuilder optionalLocalForInitobj)
- {
- switch (resultType)
- {
- case Type t when t == typeof(void):
- break;
- case Type t when t.IsClass || t.IsInterface:
- ilBuilder.Emit(OpCodes.Ldnull);
- break;
- case Type t when t.UseInitObjForReturnDefault():
- EmitInitObj(ilBuilder, resultType, optionalLocalForInitobj);
- ilBuilder.EmitLdloc(optionalLocalForInitobj);
- break;
- default:
- EmitLoadDefaultPrimitive(ilBuilder, resultType);
- break;
- }
- // IL_0000: ret
- ilBuilder.Emit(OpCodes.Ret);
- }
-
- private static bool IsInitLocalPrimitive(this Type t)
- {
- // var x = default(T):
- // C# compiler uses special logic for enum defaults and primitive defaults
- // On init local case this logic does not apply for IntPtr & UIntPtr.
-
- if (t == typeof(void))
- return true;
-
- if (t.IsEnum)
- return true;
-
- return t.IsPrimitive
- && t != typeof(IntPtr)
- && t != typeof(UIntPtr);
- }
-
- private static bool IsReturnDefaultPrimitive(this Type t)
- {
- // return default(T):
- // C# compiler uses special logic for enum defaults and primitive defaults
- // On return default special logic is applied for decimals too.
-
-
- if (t == typeof(void))
- return true;
-
- if (t.IsEnum)
- return true;
-
- return t.IsPrimitive
- || t == typeof(decimal);
- }
-
- private static bool UseInitObjForInitLocal(this Type resultType)
- {
- return resultType.IsValueType && !resultType.IsInitLocalPrimitive();
- }
-
- private static bool UseInitObjForReturnDefault(this Type resultType)
- {
- return resultType.IsValueType && !resultType.IsReturnDefaultPrimitive();
- }
-
- private static void EmitInitObj(ILGenerator ilBuilder, Type resultType, LocalBuilder optionalLocalForInitobj)
- {
- if (optionalLocalForInitobj == null)
- throw new ArgumentNullException(nameof(optionalLocalForInitobj));
-
- /*
- IL_0000: ldloca.s 0
- IL_0002: initobj [mscorlib]System.DateTime
- */
- ilBuilder.EmitLdloca(optionalLocalForInitobj);
- ilBuilder.Emit(OpCodes.Initobj, resultType);
- }
-
- private static void EmitLoadDefaultPrimitive(this ILGenerator ilBuilder, Type resultType)
- {
- var valueType = resultType;
- if (valueType.IsEnum)
- valueType = resultType.GetEnumUnderlyingType();
-
- // The primitive types are Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, IntPtr, UIntPtr, Char, Double, and Single
- // + custom logic for decimal
- switch (valueType)
- {
- case Type t0 when t0 == typeof(bool):
- case Type t1 when t1 == typeof(byte):
- case Type t2 when t2 == typeof(sbyte):
- case Type t3 when t3 == typeof(short):
- case Type t4 when t4 == typeof(ushort):
- case Type t5 when t5 == typeof(int):
- case Type t6 when t6 == typeof(uint):
- case Type t7 when t7 == typeof(char):
- ilBuilder.Emit(OpCodes.Ldc_I4_0);
- break;
- case Type t1 when t1 == typeof(ulong):
- case Type t2 when t2 == typeof(long):
- /*
- // return 0L;
- IL_0000: ldc.i4.0
- IL_0001: conv.i8
- // return 0uL;
- IL_0000: ldc.i4.0
- IL_0001: conv.i8
- */
- ilBuilder.Emit(OpCodes.Ldc_I4_0);
- ilBuilder.Emit(OpCodes.Conv_I8);
- break;
- case Type t when t == typeof(IntPtr):
- /*
- IL_0000: ldc.i4.0
- IL_0001: conv.i
- */
- ilBuilder.Emit(OpCodes.Ldc_I4_0);
- ilBuilder.Emit(OpCodes.Conv_I);
- break;
- case Type t when t == typeof(UIntPtr):
- /*
- IL_0000: ldc.i4.0
- IL_0001: conv.u
- */
- ilBuilder.Emit(OpCodes.Ldc_I4_0);
- ilBuilder.Emit(OpCodes.Conv_U);
- break;
- case Type t when t == typeof(double):
- ilBuilder.Emit(OpCodes.Ldc_R8, 0.0d);
- break;
- case Type t when t == typeof(float):
- ilBuilder.Emit(OpCodes.Ldc_R4, 0.0f);
- break;
- case Type t when t == typeof(decimal):
- /*
- // return decimal.Zero;
- IL_0011: ldsfld valuetype [mscorlib]System.Decimal [mscorlib]System.Decimal::Zero
- */
- var zeroField = typeof(decimal).GetField(nameof(decimal.Zero));
- ilBuilder.Emit(OpCodes.Ldsfld, zeroField);
- break;
- default:
- throw new NotSupportedException($"Cannot emit default for {resultType}.");
- }
- }
- }
-}
\ No newline at end of file
diff --git a/src/BenchmarkDotNet/Helpers/Reflection.Emit/IlGeneratorEmitOpExtensions.cs b/src/BenchmarkDotNet/Helpers/Reflection.Emit/IlGeneratorEmitOpExtensions.cs
index 50a3392f44..7763d59e91 100644
--- a/src/BenchmarkDotNet/Helpers/Reflection.Emit/IlGeneratorEmitOpExtensions.cs
+++ b/src/BenchmarkDotNet/Helpers/Reflection.Emit/IlGeneratorEmitOpExtensions.cs
@@ -116,127 +116,19 @@ public static void EmitLdarg(this ILGenerator ilBuilder, ParameterInfo argument)
}
}
- public static void EmitLdindStind(this ILGenerator ilBuilder, Type resultType)
+ public static void EmitStarg(this ILGenerator ilBuilder, ParameterInfo argument)
{
- if (!resultType.IsByRef)
- throw new NotSupportedException($"Cannot emit indirect op for non-reference {resultType}.");
-
- // The primitive types are Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, IntPtr, UIntPtr, Char, Double, and Single
- var valueType = resultType.GetElementType();
- if (valueType?.IsEnum ?? false)
- valueType = valueType.GetEnumUnderlyingType();
+ var position = argument.Position;
+ if (!((MethodBase) argument.Member).IsStatic)
+ position++;
- switch (valueType)
+ if (position < 255)
{
- case Type t when t == typeof(bool):
- /*
- IL_0018: ldind.u1
- IL_0019: stind.i1
- */
- ilBuilder.Emit(OpCodes.Ldind_U1);
- ilBuilder.Emit(OpCodes.Stind_I1);
- break;
- case Type t when t == typeof(byte):
- /*
- IL_0018: ldind.u1
- IL_0019: stind.i1
- */
- ilBuilder.Emit(OpCodes.Ldind_U1);
- ilBuilder.Emit(OpCodes.Stind_I1);
- break;
- case Type t when t == typeof(sbyte):
- /*
- IL_0018: ldind.i1
- IL_0019: stind.i1
- */
- ilBuilder.Emit(OpCodes.Ldind_I1);
- ilBuilder.Emit(OpCodes.Stind_I1);
- break;
- case Type t when t == typeof(short):
- /*
- IL_0018: ldind.i2
- IL_0019: stind.i2
- */
- ilBuilder.Emit(OpCodes.Ldind_I2);
- ilBuilder.Emit(OpCodes.Stind_I2);
- break;
- case Type t1 when t1 == typeof(ushort):
- case Type t2 when t2 == typeof(char):
- /*
- IL_0018: ldind.u2
- IL_0019: stind.i2
- */
- ilBuilder.Emit(OpCodes.Ldind_U2);
- ilBuilder.Emit(OpCodes.Stind_I2);
- break;
- case Type t when t == typeof(int):
- /*
- IL_0018: ldind.i4
- IL_0019: stind.i4
- */
- ilBuilder.Emit(OpCodes.Ldind_I4);
- ilBuilder.Emit(OpCodes.Stind_I4);
- break;
- case Type t when t == typeof(uint):
- /*
- IL_0018: ldind.i4
- IL_0019: stind.i4
- */
- ilBuilder.Emit(OpCodes.Ldind_U4);
- ilBuilder.Emit(OpCodes.Stind_I4);
- break;
- case Type t1 when t1 == typeof(ulong):
- case Type t2 when t2 == typeof(long):
- /*
- IL_0018: ldind.i8
- IL_0019: stind.i8
- */
- ilBuilder.Emit(OpCodes.Ldind_I8);
- ilBuilder.Emit(OpCodes.Stind_I8);
- break;
- case Type t1 when t1 == typeof(IntPtr):
- case Type t2 when t2 == typeof(UIntPtr):
- /*
- IL_0018: ldind.i
- IL_0019: stind.i
- */
- ilBuilder.Emit(OpCodes.Ldind_I);
- ilBuilder.Emit(OpCodes.Stind_I);
- break;
- case Type t when t == typeof(double):
- /*
- IL_0018: ldind.r8
- IL_0019: stind.i8
- */
- ilBuilder.Emit(OpCodes.Ldind_R8);
- ilBuilder.Emit(OpCodes.Stind_R8);
- break;
- case Type t when t == typeof(float):
- /*
- IL_0018: ldind.r4
- IL_0019: stind.i4
- */
- ilBuilder.Emit(OpCodes.Ldind_R4);
- ilBuilder.Emit(OpCodes.Stind_R4);
- break;
- case Type t when t.IsClass || t.IsInterface:
- /*
- IL_0018: ldind.ref
- IL_0019: stind.ref
- */
- ilBuilder.Emit(OpCodes.Ldind_Ref);
- ilBuilder.Emit(OpCodes.Stind_Ref);
- break;
- case Type t when t.IsEnum || t.IsValueType:
- /*
- IL_0018: ldobj valuetype [mscorlib]System.Nullable`1
- IL_0019: stobj valuetype [mscorlib]System.Nullable`1
- */
- ilBuilder.Emit(OpCodes.Ldobj, valueType);
- ilBuilder.Emit(OpCodes.Stobj, valueType);
- break;
- default:
- throw new NotSupportedException($"Cannot emit indirect store for {resultType}.");
+ ilBuilder.Emit(OpCodes.Starg_S, (byte) position);
+ }
+ else
+ {
+ ilBuilder.Emit(OpCodes.Starg, checked((short) position));
}
}
}
diff --git a/src/BenchmarkDotNet/Helpers/Reflection.Emit/IlGeneratorStatementExtensions.cs b/src/BenchmarkDotNet/Helpers/Reflection.Emit/IlGeneratorStatementExtensions.cs
index 6d601e6495..4fd23e3ab8 100644
--- a/src/BenchmarkDotNet/Helpers/Reflection.Emit/IlGeneratorStatementExtensions.cs
+++ b/src/BenchmarkDotNet/Helpers/Reflection.Emit/IlGeneratorStatementExtensions.cs
@@ -84,64 +84,57 @@ public static void EmitSetDelegateToThisField(
}
}
- public static void EmitLoopBeginFromLocToArg(
+ public static void EmitLoopBeginFromArgToZero(
this ILGenerator ilBuilder,
- Label loopStartLabel,
- Label loopHeadLabel,
- LocalBuilder indexLocal,
- ParameterInfo toArg)
+ out Label loopStartLabel,
+ out Label loopHeadLabel)
{
- // loop counter stored as loc0, loop max passed as arg1
+ loopStartLabel = ilBuilder.DefineLabel();
+ loopHeadLabel = ilBuilder.DefineLabel();
+ // invokeCount passed as arg
/*
- // for (long i = 0L; i < invokeCount; i++)
- IL_0000: ldc.i4.0
- IL_0001: conv.i8
- IL_0002: stloc.0
+ // while (--invokeCount >= 0)
*/
- ilBuilder.Emit(OpCodes.Ldc_I4_0);
- ilBuilder.Emit(OpCodes.Conv_I8);
- ilBuilder.EmitStloc(indexLocal);
- // IL_0003: br.s IL_0036 // loop head: IL_0036 // we use long jump
+ // IL_0000: br.s IL_000e // loop head: IL_000e // we use long jump
ilBuilder.Emit(OpCodes.Br, loopHeadLabel);
- // loop start (head: IL_0036)
+ // loop start (head: IL_000e)
ilBuilder.MarkLabel(loopStartLabel);
}
- public static void EmitLoopEndFromLocToArg(
+ public static void EmitLoopEndFromArgToZero(
this ILGenerator ilBuilder,
Label loopStartLabel,
Label loopHeadLabel,
- LocalBuilder indexLocal,
- ParameterInfo toArg)
+ ParameterInfo arg)
{
- // loop counter stored as loc0, loop max passed as arg1
- /*
- // for (long i = 0L; i < invokeCount; i++)
- IL_0031: ldloc.0
- IL_0032: ldc.i4.1
- IL_0033: conv.i8
- IL_0034: add
- IL_0035: stloc.0
- */
- ilBuilder.EmitLdloc(indexLocal);
- ilBuilder.Emit(OpCodes.Ldc_I4_1);
- ilBuilder.Emit(OpCodes.Conv_I8);
- ilBuilder.Emit(OpCodes.Add);
- ilBuilder.EmitStloc(indexLocal);
-
+ // invokeCount passed as arg
/*
- // for (long i = 0L; i < invokeCount; i++)
- IL_0036: ldloc.0 // loop head: IL_0036
- IL_0037: ldarg.1
- IL_0038: blt.s IL_0005 // we use long jump
+ // while (--invokeCount >= 0)
+ IL_0008: ldarg.1
+ IL_0009: ldc.i4.1
+ IL_000a: conv.i8
+ IL_000b: sub
+ IL_000c: dup
+ IL_000d: starg.s invokeCount
+ IL_000f: ldc.i4.0
+ IL_0010: conv.i8
+ IL_0011: bge.s IL_0002
// end loop
*/
+
+ // loop head
ilBuilder.MarkLabel(loopHeadLabel);
- ilBuilder.EmitLdloc(indexLocal);
- ilBuilder.EmitLdarg(toArg);
- ilBuilder.Emit(OpCodes.Blt, loopStartLabel);
+ ilBuilder.EmitLdarg(arg);
+ ilBuilder.Emit(OpCodes.Ldc_I4_1);
+ ilBuilder.Emit(OpCodes.Conv_I8);
+ ilBuilder.Emit(OpCodes.Sub);
+ ilBuilder.Emit(OpCodes.Dup);
+ ilBuilder.EmitStarg(arg);
+ ilBuilder.Emit(OpCodes.Ldc_I4_0);
+ ilBuilder.Emit(OpCodes.Conv_I8);
+ ilBuilder.Emit(OpCodes.Bge, loopStartLabel);
}
}
}
\ No newline at end of file
diff --git a/src/BenchmarkDotNet/Helpers/Reflection.Emit/MethodBuilderExtensions.cs b/src/BenchmarkDotNet/Helpers/Reflection.Emit/MethodBuilderExtensions.cs
index c09a7d19c2..ae3890141b 100644
--- a/src/BenchmarkDotNet/Helpers/Reflection.Emit/MethodBuilderExtensions.cs
+++ b/src/BenchmarkDotNet/Helpers/Reflection.Emit/MethodBuilderExtensions.cs
@@ -1,5 +1,4 @@
using BenchmarkDotNet.Portability;
-using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
@@ -9,9 +8,6 @@ namespace BenchmarkDotNet.Helpers.Reflection.Emit
{
internal static class MethodBuilderExtensions
{
- public static Type[] GetParameterTypes(this MethodBase method) =>
- method.GetParameters().Select(p => p.ParameterType).ToArray();
-
public static ParameterInfo[] GetEmitParameters(this MethodBuilder method, IEnumerable signatureParameters) =>
signatureParameters
.Select(p =>
diff --git a/src/BenchmarkDotNet/Helpers/Reflection.Emit/ModuleBuilderExtensions.cs b/src/BenchmarkDotNet/Helpers/Reflection.Emit/ModuleBuilderExtensions.cs
deleted file mode 100644
index c879bc1f46..0000000000
--- a/src/BenchmarkDotNet/Helpers/Reflection.Emit/ModuleBuilderExtensions.cs
+++ /dev/null
@@ -1,60 +0,0 @@
-using System;
-using System.Linq;
-using System.Reflection;
-using System.Reflection.Emit;
-
-namespace BenchmarkDotNet.Helpers.Reflection.Emit
-{
- internal static class ModuleBuilderExtensions
- {
- public static Type EmitCustomDelegate(
- this ModuleBuilder moduleBuilder,
- string delegateTypeName,
- ParameterInfo returnType,
- ParameterInfo[] parameters)
- {
- // TODO: begin/end invoke ?
- var delegatePatternType = typeof(Action);
-
- var typeBuilder = moduleBuilder.DefineType(
- delegateTypeName,
- delegatePatternType.Attributes,
- delegatePatternType.BaseType);
-
- var ctorPattern = delegatePatternType.GetConstructors().Single();
- var ctorBuilder = typeBuilder.DefineConstructor(
- ctorPattern.Attributes,
- ctorPattern.CallingConvention,
- ctorPattern.GetParameterTypes());
-
- foreach (var parameterInfo in ctorPattern.GetParameters())
- {
- ctorBuilder.DefineParameter(parameterInfo.Position + 1, parameterInfo.Attributes, parameterInfo.Name);
- }
-
- ctorBuilder.SetImplementationFlags(ctorPattern.GetMethodImplementationFlags());
-
- var invokePatternMethod = TypeBuilderExtensions.GetDelegateInvokeMethod(delegatePatternType);
-
- var invokeBuilder = typeBuilder.DefineMethod(
- invokePatternMethod.Name,
- invokePatternMethod.Attributes,
- invokePatternMethod.CallingConvention,
- returnType.ParameterType,
- parameters.Select(p => p.ParameterType).ToArray());
- foreach (var parameterInfo in parameters)
- {
- invokeBuilder.DefineParameter(parameterInfo.Position + 1, parameterInfo.Attributes, parameterInfo.Name);
- }
- invokeBuilder.DefineParameter(0, returnType.Attributes, "");
-
- invokeBuilder.SetImplementationFlags(invokePatternMethod.GetMethodImplementationFlags());
-
-#if NETFRAMEWORK
- return typeBuilder.CreateType();
-#else
- return typeBuilder.CreateTypeInfo();
-#endif
- }
- }
-}
\ No newline at end of file
diff --git a/src/BenchmarkDotNet/Helpers/Reflection.Emit/TypeBuilderExtensions.cs b/src/BenchmarkDotNet/Helpers/Reflection.Emit/TypeBuilderExtensions.cs
index c71c19cd63..2e290be9ba 100644
--- a/src/BenchmarkDotNet/Helpers/Reflection.Emit/TypeBuilderExtensions.cs
+++ b/src/BenchmarkDotNet/Helpers/Reflection.Emit/TypeBuilderExtensions.cs
@@ -25,15 +25,6 @@ private static void DefineParameters(this MethodBuilder methodBuilder, Parameter
methodBuilder.DefineParameter(0, returnType.Attributes, "");
}
- public static MethodInfo GetDelegateInvokeMethod(Type delegateType)
- {
- var result = delegateType.GetMethod(nameof(Action.Invoke));
- if (result == null)
- throw new ArgumentException($"The type {delegateType} nas no Invoke method.", nameof(delegateType));
-
- return result;
- }
-
public static ConstructorBuilder DefinePublicInstanceCtor(this TypeBuilder typeBuilder, params ParameterInfo[] parameters)
{
// .method public hidebysig specialname rtspecialname
diff --git a/src/BenchmarkDotNet/Templates/BenchmarkType.txt b/src/BenchmarkDotNet/Templates/BenchmarkType.txt
index f17737d646..eeeb08f3e0 100644
--- a/src/BenchmarkDotNet/Templates/BenchmarkType.txt
+++ b/src/BenchmarkDotNet/Templates/BenchmarkType.txt
@@ -1,5 +1,5 @@
// the type name must be in sync with WindowsDisassembler.BuildArguments
- public unsafe class Runnable_$ID$ : global::$WorkloadTypeName$
+ public unsafe sealed class Runnable_$ID$ : global::$WorkloadTypeName$
{
public static void Run(BenchmarkDotNet.Engines.IHost host, System.String benchmarkName)
{
@@ -51,18 +51,12 @@
}
}
- public delegate $OverheadMethodReturnTypeName$ OverheadDelegate($ArgumentsDefinition$);
-
- public delegate $WorkloadMethodReturnTypeModifiers$ $WorkloadMethodReturnType$ WorkloadDelegate($ArgumentsDefinition$);
-
public Runnable_$ID$()
{
globalSetupAction = $GlobalSetupMethodName$;
globalCleanupAction = $GlobalCleanupMethodName$;
iterationSetupAction = $IterationSetupMethodName$;
iterationCleanupAction = $IterationCleanupMethodName$;
- overheadDelegate = __Overhead;
- workloadDelegate = $WorkloadMethodDelegate$;
$InitializeArgumentFields$
}
@@ -70,8 +64,6 @@
private System.Action globalCleanupAction;
private System.Action iterationSetupAction;
private System.Action iterationCleanupAction;
- private BenchmarkDotNet.Autogenerated.Runnable_$ID$.OverheadDelegate overheadDelegate;
- private BenchmarkDotNet.Autogenerated.Runnable_$ID$.WorkloadDelegate workloadDelegate;
$DeclareArgumentFields$
// this method is used only for the disassembly diagnoser purposes
@@ -84,6 +76,16 @@
$DisassemblerEntryMethodName$();
}
+ [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoOptimization | System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
+ public void $DisassemblerEntryMethodName$()
+ {
+ if (NotEleven == 11)
+ {
+ $LoadArguments$
+ $WorkloadMethodCall$;
+ }
+ }
+
private System.Int32 dummyVar;
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
@@ -104,310 +106,48 @@
@DummyUnroll@
}
- private $OverheadMethodReturnTypeName$ __Overhead($ArgumentsDefinition$) // __ is to avoid possible name conflict
- {
- $OverheadImplementation$
- }
-
-#if RETURNS_CONSUMABLE_$ID$
-
- private BenchmarkDotNet.Engines.Consumer consumer = new BenchmarkDotNet.Engines.Consumer();
-
- [System.Runtime.CompilerServices.MethodImpl(BenchmarkDotNet.Portability.CodeGenHelper.AggressiveOptimizationOption)]
- private void OverheadActionUnroll(System.Int64 invokeCount)
- {
- $LoadArguments$
- for (System.Int64 i = 0; i < invokeCount; i++)
- {
- consumer.Consume(overheadDelegate($PassArguments$));@Unroll@
- }
- }
-
- [System.Runtime.CompilerServices.MethodImpl(BenchmarkDotNet.Portability.CodeGenHelper.AggressiveOptimizationOption)]
- private void OverheadActionNoUnroll(System.Int64 invokeCount)
- {
- $LoadArguments$
- for (System.Int64 i = 0; i < invokeCount; i++)
- {
- consumer.Consume(overheadDelegate($PassArguments$));
- }
- }
-
- [System.Runtime.CompilerServices.MethodImpl(BenchmarkDotNet.Portability.CodeGenHelper.AggressiveOptimizationOption)]
- private void WorkloadActionUnroll(System.Int64 invokeCount)
- {
- $LoadArguments$
- for (System.Int64 i = 0; i < invokeCount; i++)
- {
- consumer.Consume(workloadDelegate($PassArguments$)$ConsumeField$);@Unroll@
- }
- }
-
- [System.Runtime.CompilerServices.MethodImpl(BenchmarkDotNet.Portability.CodeGenHelper.AggressiveOptimizationOption)]
- private void WorkloadActionNoUnroll(System.Int64 invokeCount)
- {
- $LoadArguments$
- for (System.Int64 i = 0; i < invokeCount; i++)
- {
- consumer.Consume(workloadDelegate($PassArguments$)$ConsumeField$);
- }
- }
-
- [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoOptimization | System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
- public $WorkloadMethodReturnType$ $DisassemblerEntryMethodName$()
- {
- if (NotEleven == 11)
- {
- $LoadArguments$
- return $WorkloadMethodCall$;
- }
-
- return default($WorkloadMethodReturnType$);
- }
-
-#elif RETURNS_NON_CONSUMABLE_STRUCT_$ID$
-
- [System.Runtime.CompilerServices.MethodImpl(BenchmarkDotNet.Portability.CodeGenHelper.AggressiveOptimizationOption)]
- private void OverheadActionUnroll(System.Int64 invokeCount)
- {
- $LoadArguments$
- $OverheadMethodReturnTypeName$ result = default($OverheadMethodReturnTypeName$);
- for (System.Int64 i = 0; i < invokeCount; i++)
- {
- result = overheadDelegate($PassArguments$);@Unroll@
- }
- BenchmarkDotNet.Engines.DeadCodeEliminationHelper.KeepAliveWithoutBoxing(result);
- }
-
- [System.Runtime.CompilerServices.MethodImpl(BenchmarkDotNet.Portability.CodeGenHelper.AggressiveOptimizationOption)]
- private void OverheadActionNoUnroll(System.Int64 invokeCount)
- {
- $LoadArguments$
- $OverheadMethodReturnTypeName$ result = default($OverheadMethodReturnTypeName$);
- for (System.Int64 i = 0; i < invokeCount; i++)
- {
- result = overheadDelegate($PassArguments$);
- }
- BenchmarkDotNet.Engines.DeadCodeEliminationHelper.KeepAliveWithoutBoxing(result);
- }
-
- [System.Runtime.CompilerServices.MethodImpl(BenchmarkDotNet.Portability.CodeGenHelper.AggressiveOptimizationOption)]
- private void WorkloadActionUnroll(System.Int64 invokeCount)
- {
- $LoadArguments$
- $WorkloadMethodReturnType$ result = default($WorkloadMethodReturnType$);
- for (System.Int64 i = 0; i < invokeCount; i++)
- {
- result = workloadDelegate($PassArguments$);@Unroll@
- }
- NonGenericKeepAliveWithoutBoxing(result);
- }
-
- [System.Runtime.CompilerServices.MethodImpl(BenchmarkDotNet.Portability.CodeGenHelper.AggressiveOptimizationOption)]
- private void WorkloadActionNoUnroll(System.Int64 invokeCount)
- {
- $LoadArguments$
- $WorkloadMethodReturnType$ result = default($WorkloadMethodReturnType$);
- for (System.Int64 i = 0; i < invokeCount; i++)
- {
- result = workloadDelegate($PassArguments$);
- }
- NonGenericKeepAliveWithoutBoxing(result);
- }
-
- // we must not simply use DeadCodeEliminationHelper.KeepAliveWithoutBoxing because it's generic method
- // and stack-only types like Span can not be generic type arguments http://adamsitnik.com/Span/#span-must-not-be-a-generic-type-argument
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
- private void NonGenericKeepAliveWithoutBoxing($WorkloadMethodReturnType$ _) { }
-
- [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoOptimization | System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
- public $WorkloadMethodReturnType$ $DisassemblerEntryMethodName$()
+ private void __Overhead($ArgumentsDefinition$) // __ is to avoid possible name conflict
{
- if (NotEleven == 11)
- {
- $LoadArguments$
- return $WorkloadMethodCall$;
- }
-
- return default($WorkloadMethodReturnType$);
}
-#elif RETURNS_BYREF_$ID$
-
[System.Runtime.CompilerServices.MethodImpl(BenchmarkDotNet.Portability.CodeGenHelper.AggressiveOptimizationOption)]
private void OverheadActionUnroll(System.Int64 invokeCount)
{
$LoadArguments$
- $OverheadMethodReturnTypeName$ value = default($OverheadMethodReturnTypeName$);
- for (System.Int64 i = 0; i < invokeCount; i++)
+ while (--invokeCount >= 0)
{
- value = overheadDelegate($PassArguments$);@Unroll@
+ __Overhead($PassArguments$);@Unroll@
}
- BenchmarkDotNet.Engines.DeadCodeEliminationHelper.KeepAliveWithoutBoxing(value);
}
[System.Runtime.CompilerServices.MethodImpl(BenchmarkDotNet.Portability.CodeGenHelper.AggressiveOptimizationOption)]
private void OverheadActionNoUnroll(System.Int64 invokeCount)
{
$LoadArguments$
- $OverheadMethodReturnTypeName$ value = default($OverheadMethodReturnTypeName$);
- for (System.Int64 i = 0; i < invokeCount; i++)
+ while (--invokeCount >= 0)
{
- value = overheadDelegate($PassArguments$);
+ __Overhead($PassArguments$);
}
- BenchmarkDotNet.Engines.DeadCodeEliminationHelper.KeepAliveWithoutBoxing(value);
}
- private $WorkloadMethodReturnType$ workloadDefaultValueHolder = default($WorkloadMethodReturnType$);
-
[System.Runtime.CompilerServices.MethodImpl(BenchmarkDotNet.Portability.CodeGenHelper.AggressiveOptimizationOption)]
private void WorkloadActionUnroll(System.Int64 invokeCount)
{
$LoadArguments$
- ref $WorkloadMethodReturnType$ alias = ref workloadDefaultValueHolder;
- for (System.Int64 i = 0; i < invokeCount; i++)
+ while (--invokeCount >= 0)
{
- alias = workloadDelegate($PassArguments$);@Unroll@
+ $WorkloadMethodCall$;@Unroll@
}
- BenchmarkDotNet.Engines.DeadCodeEliminationHelper.KeepAliveWithoutBoxing(ref alias);
}
[System.Runtime.CompilerServices.MethodImpl(BenchmarkDotNet.Portability.CodeGenHelper.AggressiveOptimizationOption)]
private void WorkloadActionNoUnroll(System.Int64 invokeCount)
{
$LoadArguments$
- ref $WorkloadMethodReturnType$ alias = ref workloadDefaultValueHolder;
- for (System.Int64 i = 0; i < invokeCount; i++)
- {
- alias = workloadDelegate($PassArguments$);
- }
- BenchmarkDotNet.Engines.DeadCodeEliminationHelper.KeepAliveWithoutBoxing(ref alias);
- }
-
- [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoOptimization | System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
- public ref $WorkloadMethodReturnType$ $DisassemblerEntryMethodName$()
- {
- if (NotEleven == 11)
- {
- $LoadArguments$
- return ref $WorkloadMethodCall$;
- }
-
- return ref workloadDefaultValueHolder;
- }
-#elif RETURNS_BYREF_READONLY_$ID$
-
- [System.Runtime.CompilerServices.MethodImpl(BenchmarkDotNet.Portability.CodeGenHelper.AggressiveOptimizationOption)]
- private void OverheadActionUnroll(System.Int64 invokeCount)
- {
- $LoadArguments$
- $OverheadMethodReturnTypeName$ value = default($OverheadMethodReturnTypeName$);
- for (System.Int64 i = 0; i < invokeCount; i++)
+ while (--invokeCount >= 0)
{
- value = overheadDelegate($PassArguments$);@Unroll@
- }
- BenchmarkDotNet.Engines.DeadCodeEliminationHelper.KeepAliveWithoutBoxing(value);
- }
-
- [System.Runtime.CompilerServices.MethodImpl(BenchmarkDotNet.Portability.CodeGenHelper.AggressiveOptimizationOption)]
- private void OverheadActionNoUnroll(System.Int64 invokeCount)
- {
- $LoadArguments$
- $OverheadMethodReturnTypeName$ value = default($OverheadMethodReturnTypeName$);
- for (System.Int64 i = 0; i < invokeCount; i++)
- {
- value = overheadDelegate($PassArguments$);
- }
- BenchmarkDotNet.Engines.DeadCodeEliminationHelper.KeepAliveWithoutBoxing(value);
- }
-
- private $WorkloadMethodReturnType$ workloadDefaultValueHolder = default($WorkloadMethodReturnType$);
-
- [System.Runtime.CompilerServices.MethodImpl(BenchmarkDotNet.Portability.CodeGenHelper.AggressiveOptimizationOption)]
- private void WorkloadActionUnroll(System.Int64 invokeCount)
- {
- $LoadArguments$
- ref $WorkloadMethodReturnType$ alias = ref workloadDefaultValueHolder;
- for (System.Int64 i = 0; i < invokeCount; i++)
- {
- alias = workloadDelegate($PassArguments$);@Unroll@
- }
- BenchmarkDotNet.Engines.DeadCodeEliminationHelper.KeepAliveWithoutBoxingReadonly(alias);
- }
-
- [System.Runtime.CompilerServices.MethodImpl(BenchmarkDotNet.Portability.CodeGenHelper.AggressiveOptimizationOption)]
- private void WorkloadActionNoUnroll(System.Int64 invokeCount)
- {
- $LoadArguments$
- ref $WorkloadMethodReturnType$ alias = ref workloadDefaultValueHolder;
- for (System.Int64 i = 0; i < invokeCount; i++)
- {
- alias = workloadDelegate($PassArguments$);
- }
- BenchmarkDotNet.Engines.DeadCodeEliminationHelper.KeepAliveWithoutBoxingReadonly(alias);
- }
-
- [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoOptimization | System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
- public ref readonly $WorkloadMethodReturnType$ $DisassemblerEntryMethodName$()
- {
- if (NotEleven == 11)
- {
- $LoadArguments$
- return ref $WorkloadMethodCall$;
- }
-
- return ref workloadDefaultValueHolder;
- }
-#elif RETURNS_VOID_$ID$
-
- [System.Runtime.CompilerServices.MethodImpl(BenchmarkDotNet.Portability.CodeGenHelper.AggressiveOptimizationOption)]
- private void OverheadActionUnroll(System.Int64 invokeCount)
- {
- $LoadArguments$
- for (System.Int64 i = 0; i < invokeCount; i++)
- {
- overheadDelegate($PassArguments$);@Unroll@
- }
- }
-
- [System.Runtime.CompilerServices.MethodImpl(BenchmarkDotNet.Portability.CodeGenHelper.AggressiveOptimizationOption)]
- private void OverheadActionNoUnroll(System.Int64 invokeCount)
- {
- $LoadArguments$
- for (System.Int64 i = 0; i < invokeCount; i++)
- {
- overheadDelegate($PassArguments$);
- }
- }
-
- [System.Runtime.CompilerServices.MethodImpl(BenchmarkDotNet.Portability.CodeGenHelper.AggressiveOptimizationOption)]
- private void WorkloadActionUnroll(System.Int64 invokeCount)
- {
- $LoadArguments$
- for (System.Int64 i = 0; i < invokeCount; i++)
- {
- workloadDelegate($PassArguments$);@Unroll@
- }
- }
-
- [System.Runtime.CompilerServices.MethodImpl(BenchmarkDotNet.Portability.CodeGenHelper.AggressiveOptimizationOption)]
- private void WorkloadActionNoUnroll(System.Int64 invokeCount)
- {
- $LoadArguments$
- for (System.Int64 i = 0; i < invokeCount; i++)
- {
- workloadDelegate($PassArguments$);
- }
- }
-
- [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoOptimization | System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
- public void $DisassemblerEntryMethodName$()
- {
- if (NotEleven == 11)
- {
- $LoadArguments$
$WorkloadMethodCall$;
}
}
-#endif // RETURNS
}
\ No newline at end of file
diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/ConsumableTypeInfo.cs b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/ConsumableTypeInfo.cs
index 147a514058..123f07bf7a 100644
--- a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/ConsumableTypeInfo.cs
+++ b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/ConsumableTypeInfo.cs
@@ -1,7 +1,4 @@
-using BenchmarkDotNet.Engines;
-using System;
-using System.Collections.Generic;
-using System.Linq;
+using System;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
@@ -41,43 +38,23 @@ public ConsumableTypeInfo(Type methodReturnType)
if (WorkloadMethodReturnType == null)
throw new InvalidOperationException("Bug: (WorkloadMethodReturnType == null");
- var consumableField = default(FieldInfo);
if (WorkloadMethodReturnType == typeof(void))
{
IsVoid = true;
- OverheadMethodReturnType = WorkloadMethodReturnType;
}
else if (WorkloadMethodReturnType.IsByRef)
{
IsByRef = true;
- OverheadMethodReturnType = typeof(IntPtr);
}
- else if (Consumer.IsConsumable(WorkloadMethodReturnType)
- || Consumer.HasConsumableField(WorkloadMethodReturnType, out consumableField))
- {
- IsConsumable = true;
- WorkloadConsumableField = consumableField;
- OverheadMethodReturnType = consumableField?.FieldType ?? WorkloadMethodReturnType;
- }
- else
- {
- OverheadMethodReturnType = typeof(int); // we return this simple type because creating bigger ValueType could take longer than benchmarked method itself
- }
-
- if (OverheadMethodReturnType == null)
- throw new InvalidOperationException("Bug: (OverheadResultType == null");
}
public Type OriginMethodReturnType { get; }
public Type WorkloadMethodReturnType { get; }
- public Type OverheadMethodReturnType { get; }
public MethodInfo? GetResultMethod { get; }
public bool IsVoid { get; }
public bool IsByRef { get; }
- public bool IsConsumable { get; }
- public FieldInfo? WorkloadConsumableField { get; }
public bool IsAwaitable { get; }
}
diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/ByRefConsumeEmitter.cs b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/ByRefConsumeEmitter.cs
deleted file mode 100644
index 96ed6d9578..0000000000
--- a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/ByRefConsumeEmitter.cs
+++ /dev/null
@@ -1,152 +0,0 @@
-using System;
-using System.Linq;
-using System.Reflection;
-using System.Reflection.Emit;
-using BenchmarkDotNet.Engines;
-using BenchmarkDotNet.Helpers.Reflection.Emit;
-using static BenchmarkDotNet.Toolchains.InProcess.Emit.Implementation.RunnableConstants;
-
-namespace BenchmarkDotNet.Toolchains.InProcess.Emit.Implementation
-{
- internal class ByRefConsumeEmitter : ConsumeEmitter
- {
- private FieldBuilder workloadDefaultValueHolderField;
- private MethodInfo overheadKeepAliveWithoutBoxingMethod;
- private MethodInfo workloadKeepAliveWithoutBoxingMethod;
- private LocalBuilder resultLocal;
-
- public ByRefConsumeEmitter(ConsumableTypeInfo consumableTypeInfo) : base(consumableTypeInfo) { }
-
- protected override void OnDefineFieldsOverride(TypeBuilder runnableBuilder)
- {
- var nonRefType = ConsumableInfo.WorkloadMethodReturnType.GetElementType();
- if (nonRefType == null)
- throw new InvalidOperationException($"Bug: type {ConsumableInfo.WorkloadMethodReturnType} is non-ref type.");
-
- workloadDefaultValueHolderField = runnableBuilder.DefineField(
- WorkloadDefaultValueHolderFieldName,
- nonRefType, FieldAttributes.Private);
- }
-
- protected override void EmitDisassemblyDiagnoserReturnDefaultOverride(ILGenerator ilBuilder)
- {
- /*
- // return ref workloadDefaultValueHolder;
- IL_0031: ldarg.0
- IL_0032: ldflda int32 BenchmarkDotNet.Autogenerated.Runnable_0::workloadDefaultValueHolder
- IL_0037: ret
- */
- ilBuilder.Emit(OpCodes.Ldarg_0);
- ilBuilder.Emit(OpCodes.Ldflda, workloadDefaultValueHolderField);
- ilBuilder.Emit(OpCodes.Ret);
- }
-
- protected override void OnEmitMembersOverride(TypeBuilder runnableBuilder)
- {
- overheadKeepAliveWithoutBoxingMethod = typeof(DeadCodeEliminationHelper).GetMethods()
- .First(m => m.Name == nameof(DeadCodeEliminationHelper.KeepAliveWithoutBoxing)
- && m.GetParameterTypes().First().IsByRef == false)
- .MakeGenericMethod(ConsumableInfo.OverheadMethodReturnType);
-
- workloadKeepAliveWithoutBoxingMethod = typeof(DeadCodeEliminationHelper).GetMethods()
- .First(m => m.Name == nameof(DeadCodeEliminationHelper.KeepAliveWithoutBoxing)
- && m.GetParameterTypes().First().IsByRef)
- .MakeGenericMethod(ConsumableInfo.WorkloadMethodReturnType.GetElementType());
- }
-
- protected override void DeclareActionLocalsOverride(ILGenerator ilBuilder)
- {
- /*
- .locals init (
- [4] native int,
- )
- -or-
- .locals init (
- [4] int32&,
- )
- */
- if (ActionKind == RunnableActionKind.Overhead)
- resultLocal = ilBuilder.DeclareLocal(ConsumableInfo.OverheadMethodReturnType);
- else
- resultLocal = ilBuilder.DeclareLocal(ConsumableInfo.WorkloadMethodReturnType);
- }
-
- /// Emits the action before loop override.
- /// The il builder.
- /// EmitActionKind - null
- protected override void EmitActionBeforeLoopOverride(ILGenerator ilBuilder)
- {
- /*
- // IntPtr value = default(IntPtr);
- IL_001c: ldloca.s 4
- IL_001e: initobj [mscorlib]System.IntPtr
- -or-
- // ref int reference = ref workloadDefaultValueHolder;
- IL_001c: ldarg.0
- IL_001d: ldflda int32 BenchmarkDotNet.Autogenerated.Runnable_0::workloadDefaultValueHolder
- IL_0022: stloc.s 4
- */
- if (ActionKind == RunnableActionKind.Overhead)
- {
- ilBuilder.EmitLdloca(resultLocal);
- ilBuilder.Emit(OpCodes.Initobj, ConsumableInfo.OverheadMethodReturnType);
- }
- else
- {
- ilBuilder.Emit(OpCodes.Ldarg_0);
- ilBuilder.Emit(OpCodes.Ldflda, workloadDefaultValueHolderField);
- ilBuilder.EmitStloc(resultLocal);
- }
- }
-
- protected override void EmitActionBeforeCallOverride(ILGenerator ilBuilder)
- {
- /*
-
- -or-
- // reference = ...
- IL_002a: ldloc.s 4
- */
- if (ActionKind != RunnableActionKind.Overhead)
- {
- ilBuilder.EmitLdloc(resultLocal);
- }
- }
-
- protected override void EmitActionAfterCallOverride(ILGenerator ilBuilder)
- {
- /*
- IL_0039: stloc.s 4
- -or-
- // reference = ...
- IL_003b: ldind.i4
- IL_003c: stind.i4
- */
- if (ActionKind == RunnableActionKind.Overhead)
- {
- ilBuilder.EmitStloc(resultLocal);
- }
- else
- {
- ilBuilder.EmitLdindStind(resultLocal.LocalType);
- }
- }
-
- protected override void EmitActionAfterLoopOverride(ILGenerator ilBuilder)
- {
- /*
- // DeadCodeEliminationHelper.KeepAliveWithoutBoxing(value);
- IL_007a: ldloc.s 4
- IL_007c: call void [BenchmarkDotNet]BenchmarkDotNet.Engines.DeadCodeEliminationHelper::KeepAliveWithoutBoxing(!!0)
- -or-
- // DeadCodeEliminationHelper.KeepAliveWithoutBoxing(ref reference);
- IL_0082: ldloc.s 4
- IL_0084: call void [BenchmarkDotNet]BenchmarkDotNet.Engines.DeadCodeEliminationHelper::KeepAliveWithoutBoxing(!!0&)
- */
- if (ActionKind == RunnableActionKind.Overhead)
- ilBuilder.EmitStaticCall(overheadKeepAliveWithoutBoxingMethod, resultLocal);
- else
- ilBuilder.EmitStaticCall(workloadKeepAliveWithoutBoxingMethod, resultLocal);
- }
- }
-}
\ No newline at end of file
diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/ConsumableConsumeEmitter.cs b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/ConsumableConsumeEmitter.cs
deleted file mode 100644
index 76a2a5f505..0000000000
--- a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/ConsumableConsumeEmitter.cs
+++ /dev/null
@@ -1,135 +0,0 @@
-using System;
-using System.Linq;
-using System.Reflection;
-using System.Reflection.Emit;
-using BenchmarkDotNet.Engines;
-using BenchmarkDotNet.Helpers.Reflection.Emit;
-
-namespace BenchmarkDotNet.Toolchains.InProcess.Emit.Implementation
-{
- internal class ConsumableConsumeEmitter : ConsumeEmitter
- {
- private static MethodInfo GetConsumeMethod(Type consumableType)
- {
- var consumeMethod = typeof(Consumer).GetMethod(nameof(Consumer.Consume), new[] { consumableType });
-
- // Use generic method for ref types
- if (consumeMethod == null || consumeMethod.GetParameterTypes().FirstOrDefault() == typeof(object))
- {
- if (consumableType.IsClass || consumableType.IsInterface)
- {
- consumeMethod = typeof(Consumer)
- .GetMethods()
- .Single(m =>
- {
- Type argType = m.GetParameterTypes().FirstOrDefault();
-
- return m.Name == nameof(Consumer.Consume) && m.IsGenericMethodDefinition
- && !argType.IsByRef // we are not interested in "Consume(in T value)"
- && argType.IsPointer == consumableType.IsPointer; // use "Consume(T objectValue) where T : class" or "Consume(T* ptrValue) where T: unmanaged"
- });
-
- consumeMethod = consumableType.IsPointer
- ? consumeMethod.MakeGenericMethod(consumableType.GetElementType()) // consumableType is T*, we need T for Consume(T* ptrValue)
- : consumeMethod.MakeGenericMethod(consumableType);
- }
- else
- {
- consumeMethod = null;
- }
- }
-
- if (consumeMethod == null)
- {
- throw new InvalidOperationException($"Cannot consume result of {consumableType}.");
- }
-
- return consumeMethod;
- }
-
- private FieldBuilder consumerField;
- private LocalBuilder disassemblyDiagnoserLocal;
-
- public ConsumableConsumeEmitter(ConsumableTypeInfo consumableTypeInfo) : base(consumableTypeInfo)
- {
- }
-
- protected override void OnDefineFieldsOverride(TypeBuilder runnableBuilder)
- {
- consumerField = runnableBuilder.DefineField(RunnableConstants.ConsumerFieldName, typeof(Consumer), FieldAttributes.Private);
- }
-
- protected override void DeclareDisassemblyDiagnoserLocalsOverride(ILGenerator ilBuilder)
- {
- // optional local if default(T) uses .initobj
- disassemblyDiagnoserLocal = ilBuilder.DeclareOptionalLocalForReturnDefault(ConsumableInfo.WorkloadMethodReturnType);
- }
-
- protected override void EmitDisassemblyDiagnoserReturnDefaultOverride(ILGenerator ilBuilder)
- {
- ilBuilder.EmitReturnDefault(ConsumableInfo.WorkloadMethodReturnType, disassemblyDiagnoserLocal);
- }
-
- protected override void OnEmitCtorBodyOverride(ConstructorBuilder constructorBuilder, ILGenerator ilBuilder)
- {
- var ctor = typeof(Consumer).GetConstructor(Array.Empty());
- if (ctor == null)
- throw new InvalidOperationException($"Cannot get default .ctor for {typeof(Consumer)}");
-
- /*
- // consumer = new Consumer();
- IL_0000: ldarg.0
- IL_0001: newobj instance void [BenchmarkDotNet]BenchmarkDotNet.Engines.Consumer::.ctor()
- IL_0006: stfld class [BenchmarkDotNet]BenchmarkDotNet.Engines.Consumer BenchmarkDotNet.Autogenerated.Runnable_0::consumer
- */
- ilBuilder.Emit(OpCodes.Ldarg_0);
- ilBuilder.Emit(OpCodes.Newobj, ctor);
- ilBuilder.Emit(OpCodes.Stfld, consumerField);
- }
-
- protected override void EmitActionBeforeCallOverride(ILGenerator ilBuilder)
- {
- /*
- // consumer. ...;
- IL_000c: ldarg.0
- IL_000d: ldfld class [BenchmarkDotNet]BenchmarkDotNet.Engines.Consumer BenchmarkDotNet.Autogenerated.Runnable_0::consumer
- */
- ilBuilder.Emit(OpCodes.Ldarg_0);
- ilBuilder.Emit(OpCodes.Ldfld, consumerField);
- }
-
- protected override void EmitActionAfterCallOverride(ILGenerator ilBuilder)
- {
- /*
- // ... .Consume( ... )
- IL_001e: callvirt instance void [BenchmarkDotNet]BenchmarkDotNet.Engines.Consumer::Consume(string)
- -or-
- // ... .Consume( ... .ConsumableField);
- IL_001e: callvirt instance void [BenchmarkDotNet]BenchmarkDotNet.Engines.Consumer::Consume(int32)
- // -or- .Consume( ... );
- IL_001e: ldfld int32 BenchmarkDotNet.Samples.CustomWithConsumable::ConsumableField
- IL_0023: callvirt instance void [BenchmarkDotNet]BenchmarkDotNet.Engines.Consumer::Consume(int32)
- */
- if (ActionKind == RunnableActionKind.Overhead)
- {
- var overheadConsumeMethod = GetConsumeMethod(ConsumableInfo.OverheadMethodReturnType);
- ilBuilder.Emit(OpCodes.Callvirt, overheadConsumeMethod);
- }
- else
- {
- var consumeField = ConsumableInfo.WorkloadConsumableField;
- if (consumeField == null)
- {
- var consumeMethod = GetConsumeMethod(ConsumableInfo.WorkloadMethodReturnType);
- ilBuilder.Emit(OpCodes.Callvirt, consumeMethod);
- }
- else
- {
- var consumeMethod = GetConsumeMethod(consumeField.FieldType);
- ilBuilder.Emit(OpCodes.Ldfld, consumeField);
- ilBuilder.Emit(OpCodes.Callvirt, consumeMethod);
- }
- }
- }
- }
-}
\ No newline at end of file
diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/ConsumeEmitter.cs b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/ConsumeEmitter.cs
deleted file mode 100644
index 9767cb8263..0000000000
--- a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/ConsumeEmitter.cs
+++ /dev/null
@@ -1,227 +0,0 @@
-using System;
-using System.Reflection;
-using System.Reflection.Emit;
-using JetBrains.Annotations;
-
-namespace BenchmarkDotNet.Toolchains.InProcess.Emit.Implementation
-{
- internal abstract class ConsumeEmitter
- {
- public static ConsumeEmitter GetConsumeEmitter(ConsumableTypeInfo consumableTypeInfo)
- {
- if (consumableTypeInfo == null)
- throw new ArgumentNullException(nameof(consumableTypeInfo));
-
- if (consumableTypeInfo.IsVoid)
- return new VoidConsumeEmitter(consumableTypeInfo);
- if (consumableTypeInfo.IsByRef)
- return new ByRefConsumeEmitter(consumableTypeInfo);
- if (consumableTypeInfo.IsConsumable)
- return new ConsumableConsumeEmitter(consumableTypeInfo);
- return new NonConsumableConsumeEmitter(consumableTypeInfo);
- }
-
- protected ConsumeEmitter(ConsumableTypeInfo consumableTypeInfo)
- {
- if (consumableTypeInfo == null)
- throw new ArgumentNullException(nameof(consumableTypeInfo));
-
- ConsumableInfo = consumableTypeInfo;
- }
-
- protected ConsumableTypeInfo ConsumableInfo { get; }
-
- protected ILGenerator? IlBuilder { get; private set; }
- protected MethodBuilder? ActionMethodBuilder { get; private set; }
- protected MethodInfo? ActionInvokeMethod { get; private set; }
- protected RunnableActionKind? ActionKind { get; private set; }
-
- [AssertionMethod]
- private void AssertNoBuilder()
- {
- if (IlBuilder != null)
- throw new InvalidOperationException("Bug: emit action logic is broken. Expects that IlBuilder != null");
-
- if (ActionMethodBuilder != null)
- throw new InvalidOperationException(
- $"Bug: emit action logic is broken. {nameof(ActionMethodBuilder)} is not null.");
-
- if (ActionInvokeMethod != null)
- throw new InvalidOperationException(
- $"Bug: emit action logic is broken. {nameof(ActionInvokeMethod)} is not null.");
-
- if (ActionKind != null)
- throw new InvalidOperationException(
- $"Bug: emit action logic is broken. {nameof(ActionKind)} is not null.");
- }
-
- [AssertionMethod]
- private void AssertHasBuilder(ILGenerator ilBuilder)
- {
- if (IlBuilder != ilBuilder)
- throw new InvalidOperationException(
- "Bug: emit action logic is broken. Expects that IlBuilder is same as passed one.");
-
- if (ActionMethodBuilder == null)
- throw new InvalidOperationException(
- $"Bug: emit action logic is broken. {nameof(ActionMethodBuilder)} is null.");
-
- if (ActionInvokeMethod == null)
- throw new InvalidOperationException(
- $"Bug: emit action logic is broken. {nameof(ActionInvokeMethod)} is null.");
-
- if (ActionKind != RunnableActionKind.Overhead && ActionKind != RunnableActionKind.Workload)
- throw new InvalidOperationException(
- $"Bug: emit action logic is broken. Unknown {nameof(ActionKind)} value: {ActionKind}.");
- }
-
- public void OnDefineFields(TypeBuilder runnableBuilder)
- {
- AssertNoBuilder();
-
- OnDefineFieldsOverride(runnableBuilder);
- }
-
- protected virtual void OnDefineFieldsOverride(TypeBuilder runnableBuilder)
- {
- }
-
- public void OnEmitMembers(TypeBuilder runnableBuilder)
- {
- AssertNoBuilder();
-
- OnEmitMembersOverride(runnableBuilder);
- }
-
- protected virtual void OnEmitMembersOverride(TypeBuilder runnableBuilder)
- {
- }
-
- public void OnEmitCtorBody(ConstructorBuilder constructorBuilder, ILGenerator ilBuilder)
- {
- AssertNoBuilder();
-
- OnEmitCtorBodyOverride(constructorBuilder, ilBuilder);
- }
-
- protected virtual void OnEmitCtorBodyOverride(ConstructorBuilder constructorBuilder, ILGenerator ilBuilder)
- {
- }
-
- public void DeclareDisassemblyDiagnoserLocals(ILGenerator ilBuilder)
- {
- AssertNoBuilder();
-
- DeclareDisassemblyDiagnoserLocalsOverride(ilBuilder);
- }
-
- protected virtual void DeclareDisassemblyDiagnoserLocalsOverride(ILGenerator ilBuilder)
- {
- }
-
- public void EmitDisassemblyDiagnoserReturnDefault(ILGenerator ilBuilder)
- {
- AssertNoBuilder();
-
- EmitDisassemblyDiagnoserReturnDefaultOverride(ilBuilder);
- }
-
- protected virtual void EmitDisassemblyDiagnoserReturnDefaultOverride(ILGenerator ilBuilder)
- {
- }
-
- public void BeginEmitAction(
- MethodBuilder actionMethodBuilder,
- ILGenerator ilBuilder,
- MethodInfo actionInvokeMethod,
- RunnableActionKind actionKind)
- {
- if (actionMethodBuilder.IsStatic)
- throw new NotSupportedException($"The {actionMethodBuilder} method should be instance method.");
-
- AssertNoBuilder();
-
- IlBuilder = ilBuilder;
- ActionMethodBuilder = actionMethodBuilder;
- ActionInvokeMethod = actionInvokeMethod;
- ActionKind = actionKind;
-
- BeginEmitActionOverride(IlBuilder);
- }
-
- protected virtual void BeginEmitActionOverride(ILGenerator ilBuilder)
- {
- }
-
- public void CompleteEmitAction(ILGenerator ilBuilder)
- {
- AssertHasBuilder(ilBuilder);
-
- CompleteEmitActionOverride(ilBuilder);
-
- IlBuilder = null;
- ActionMethodBuilder = null;
- ActionInvokeMethod = null;
- ActionKind = null;
- }
-
- protected virtual void CompleteEmitActionOverride(ILGenerator ilBuilder)
- {
- }
-
- public void DeclareActionLocals(ILGenerator ilBuilder)
- {
- AssertHasBuilder(ilBuilder);
-
- DeclareActionLocalsOverride(ilBuilder);
- }
-
- protected virtual void DeclareActionLocalsOverride(ILGenerator ilBuilder)
- {
- }
-
- public void EmitActionBeforeLoop(ILGenerator ilBuilder)
- {
- AssertHasBuilder(ilBuilder);
-
- EmitActionBeforeLoopOverride(ilBuilder);
- }
-
- protected virtual void EmitActionBeforeLoopOverride(ILGenerator ilBuilder)
- {
- }
-
- public void EmitActionAfterLoop(ILGenerator ilBuilder)
- {
- AssertHasBuilder(ilBuilder);
-
- EmitActionAfterLoopOverride(ilBuilder);
- }
-
- protected virtual void EmitActionAfterLoopOverride(ILGenerator ilBuilder)
- {
- }
-
- public void EmitActionBeforeCall(ILGenerator ilBuilder)
- {
- AssertHasBuilder(ilBuilder);
-
- EmitActionBeforeCallOverride(ilBuilder);
- }
-
- protected virtual void EmitActionBeforeCallOverride(ILGenerator ilBuilder)
- {
- }
-
- public void EmitActionAfterCall(ILGenerator ilBuilder)
- {
- AssertHasBuilder(ilBuilder);
-
- EmitActionAfterCallOverride(ilBuilder);
- }
-
- protected virtual void EmitActionAfterCallOverride(ILGenerator ilBuilder)
- {
- }
- }
-}
\ No newline at end of file
diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/NonConsumableConsumeEmitter.cs b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/NonConsumableConsumeEmitter.cs
deleted file mode 100644
index 4087165f3b..0000000000
--- a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/NonConsumableConsumeEmitter.cs
+++ /dev/null
@@ -1,145 +0,0 @@
-using System;
-using System.Linq;
-using System.Reflection;
-using System.Reflection.Emit;
-using BenchmarkDotNet.Engines;
-using BenchmarkDotNet.Helpers.Reflection.Emit;
-using static BenchmarkDotNet.Toolchains.InProcess.Emit.Implementation.RunnableConstants;
-
-namespace BenchmarkDotNet.Toolchains.InProcess.Emit.Implementation
-{
- internal class NonConsumableConsumeEmitter : ConsumeEmitter
- {
- private MethodInfo overheadKeepAliveWithoutBoxingMethod;
- private MethodInfo nonGenericKeepAliveWithoutBoxingMethod;
- private LocalBuilder resultLocal;
- private LocalBuilder disassemblyDiagnoserLocal;
-
- public NonConsumableConsumeEmitter(ConsumableTypeInfo consumableTypeInfo) : base(consumableTypeInfo)
- {
- }
-
- protected override void OnEmitMembersOverride(TypeBuilder runnableBuilder)
- {
- overheadKeepAliveWithoutBoxingMethod = typeof(DeadCodeEliminationHelper).GetMethods()
- .First(m => m.Name == nameof(DeadCodeEliminationHelper.KeepAliveWithoutBoxing)
- && !m.GetParameterTypes().First().IsByRef)
- .MakeGenericMethod(ConsumableInfo.OverheadMethodReturnType);
-
- // we must not simply use DeadCodeEliminationHelper.KeepAliveWithoutBoxing because it's generic method
- // and stack-only types like Span can not be generic type arguments http://adamsitnik.com/Span/#span-must-not-be-a-generic-type-argument
- nonGenericKeepAliveWithoutBoxingMethod = EmitNonGenericKeepAliveWithoutBoxing(
- NonGenericKeepAliveWithoutBoxingMethodName,
- runnableBuilder);
- }
-
- protected override void DeclareDisassemblyDiagnoserLocalsOverride(ILGenerator ilBuilder)
- {
- // optional local if default(T) uses .initobj
- disassemblyDiagnoserLocal = ilBuilder.DeclareOptionalLocalForReturnDefault(ConsumableInfo.WorkloadMethodReturnType);
- }
-
- protected override void EmitDisassemblyDiagnoserReturnDefaultOverride(ILGenerator ilBuilder)
- {
- ilBuilder.EmitReturnDefault(ConsumableInfo.WorkloadMethodReturnType, disassemblyDiagnoserLocal);
- }
-
- private MethodBuilder EmitNonGenericKeepAliveWithoutBoxing(string methodName, TypeBuilder runnableBuilder)
- {
- /*
- method private hidebysig
- instance void NonGenericKeepAliveWithoutBoxing(
- valuetype BenchmarkDotNet.Samples.CustomStructNonConsumable _
- ) cil managed noinlining
- */
- var valueArg = new EmitParameterInfo(
- 0,
- DummyParamName,
- ConsumableInfo.WorkloadMethodReturnType);
- var methodBuilder = runnableBuilder.DefineNonVirtualInstanceMethod(
- methodName,
- MethodAttributes.Private,
- EmitParameterInfo.CreateReturnVoidParameter(),
- valueArg)
- .SetNoInliningImplementationFlag();
- valueArg.SetMember(methodBuilder);
-
- var ilBuilder = methodBuilder.GetILGenerator();
-
- /*
- IL_0001: ret
- */
- ilBuilder.EmitVoidReturn(methodBuilder);
-
- return methodBuilder;
- }
-
-
- protected override void DeclareActionLocalsOverride(ILGenerator ilBuilder)
- {
- /*
- .locals init (
- [2] int32
- )
- -or-
- .locals init (
- [2] valuetype BenchmarkDotNet.Samples.CustomStructNonConsumable,
- )
- */
- if (ActionKind == RunnableActionKind.Overhead)
- resultLocal = ilBuilder.DeclareLocal(ConsumableInfo.OverheadMethodReturnType);
- else
- resultLocal = ilBuilder.DeclareLocal(ConsumableInfo.WorkloadMethodReturnType);
- }
-
- /// Emits the action before loop override.
- /// The il builder.
- /// EmitActionKind - null
- protected override void EmitActionBeforeLoopOverride(ILGenerator ilBuilder)
- {
- /*
- // int value = 0;
- IL_000e: ldc.i4.0
- IL_000f: stloc.2
- -or-
- // CustomStructNonConsumable _ = default(CustomStructNonConsumable);
- IL_000e: ldloca.s 2
- IL_0010: initobj BenchmarkDotNet.Samples.CustomStructNonConsumable
- */
- ilBuilder.EmitSetLocalToDefault(resultLocal);
- }
-
- protected override void EmitActionAfterCallOverride(ILGenerator ilBuilder)
- {
- // IL_0022: stloc.2
- ilBuilder.EmitStloc(resultLocal);
- }
-
- protected override void EmitActionAfterLoopOverride(ILGenerator ilBuilder)
- {
- /*
- // DeadCodeEliminationHelper.KeepAliveWithoutBoxing(value);
- IL_002c: ldloc.2
- IL_002d: call void [BenchmarkDotNet]BenchmarkDotNet.Engines.DeadCodeEliminationHelper::KeepAliveWithoutBoxing(!!0)
- -or-
- // NonGenericKeepAliveWithoutBoxing(_);
- IL_0032: ldarg.0
- IL_0033: ldloc.2
- IL_0034: call instance void BenchmarkDotNet.Autogenerated.Runnable_0::NonGenericKeepAliveWithoutBoxing(valuetype BenchmarkDotNet.Samples.CustomStructNonConsumable)
- */
- if (ActionKind == RunnableActionKind.Overhead)
- {
- ilBuilder.EmitStaticCall(overheadKeepAliveWithoutBoxingMethod, resultLocal);
- }
- else
- {
- ilBuilder.Emit(OpCodes.Ldarg_0);
- ilBuilder.EmitInstanceCallThisValueOnStack(
- null,
- nonGenericKeepAliveWithoutBoxingMethod,
- new[] { resultLocal },
- forceDirectCall: true);
- }
- }
- }
-}
\ No newline at end of file
diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/RunnableEmitter.cs b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/RunnableEmitter.cs
index 0e9d386a24..a87d4256e2 100644
--- a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/RunnableEmitter.cs
+++ b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/RunnableEmitter.cs
@@ -170,7 +170,7 @@ private static TypeBuilder DefineRunnableTypeBuilder(
BenchmarkBuildInfo benchmark,
ModuleBuilder moduleBuilder)
{
- // .class public auto ansi beforefieldinit BenchmarkDotNet.Autogenerated.Runnable_0
+ // .class public auto ansi sealed beforefieldinit BenchmarkDotNet.Autogenerated.Runnable_0
// extends [BenchmarkDotNet]BenchmarkDotNet.Samples.SampleBenchmark
var benchmarkDescriptor = benchmark.BenchmarkCase.Descriptor;
@@ -184,7 +184,7 @@ private static TypeBuilder DefineRunnableTypeBuilder(
}
var result = moduleBuilder.DefineType(
GetRunnableTypeName(benchmark),
- workloadTypeAttributes,
+ workloadTypeAttributes | TypeAttributes.Sealed,
workloadType);
return result;
@@ -242,11 +242,8 @@ private static void EmitNoArgsMethodCallPopReturn(
private int jobUnrollFactor;
private int dummyUnrollFactor;
- private Type overheadDelegateType;
- private Type workloadDelegateType;
private TypeBuilder runnableBuilder;
private ConsumableTypeInfo consumableInfo;
- private ConsumeEmitter consumeEmitter;
private ConsumableTypeInfo globalSetupReturnInfo;
private ConsumableTypeInfo globalCleanupReturnInfo;
private ConsumableTypeInfo iterationSetupReturnInfo;
@@ -256,8 +253,6 @@ private static void EmitNoArgsMethodCallPopReturn(
private FieldBuilder globalCleanupActionField;
private FieldBuilder iterationSetupActionField;
private FieldBuilder iterationCleanupActionField;
- private FieldBuilder overheadDelegateField;
- private FieldBuilder workloadDelegateField;
private FieldBuilder notElevenField;
private FieldBuilder dummyVarField;
@@ -267,7 +262,6 @@ private static void EmitNoArgsMethodCallPopReturn(
private MethodBuilder dummy1Method;
private MethodBuilder dummy2Method;
private MethodBuilder dummy3Method;
- private MethodInfo workloadImplementationMethod;
private MethodBuilder overheadImplementationMethod;
private MethodBuilder overheadActionUnrollMethod;
private MethodBuilder overheadActionNoUnrollMethod;
@@ -316,16 +310,12 @@ private Type EmitRunnableCore(BenchmarkBuildInfo newBenchmark)
dummy2Method = EmitDummyMethod(Dummy2MethodName, dummyUnrollFactor);
dummy3Method = EmitDummyMethod(Dummy3MethodName, dummyUnrollFactor);
- // 3. Emit impl
- consumeEmitter.OnEmitMembers(runnableBuilder);
-
// Overhead impl
overheadImplementationMethod = EmitOverheadImplementation(OverheadImplementationMethodName);
overheadActionUnrollMethod = EmitOverheadAction(OverheadActionUnrollMethodName, jobUnrollFactor);
overheadActionNoUnrollMethod = EmitOverheadAction(OverheadActionNoUnrollMethodName, 1);
// Workload impl
- workloadImplementationMethod = EmitWorkloadImplementation(WorkloadImplementationMethodName);
workloadActionUnrollMethod = EmitWorkloadAction(WorkloadActionUnrollMethodName, jobUnrollFactor);
workloadActionNoUnrollMethod = EmitWorkloadAction(WorkloadActionNoUnrollMethodName, 1);
@@ -361,16 +351,13 @@ private void InitForEmitRunnable(BenchmarkBuildInfo newBenchmark)
dummyUnrollFactor = DummyUnrollFactor;
consumableInfo = new ConsumableTypeInfo(benchmark.BenchmarkCase.Descriptor.WorkloadMethod.ReturnType);
- consumeEmitter = ConsumeEmitter.GetConsumeEmitter(consumableInfo);
globalSetupReturnInfo = GetConsumableTypeInfo(benchmark.BenchmarkCase.Descriptor.GlobalSetupMethod?.ReturnType);
globalCleanupReturnInfo = GetConsumableTypeInfo(benchmark.BenchmarkCase.Descriptor.GlobalCleanupMethod?.ReturnType);
iterationSetupReturnInfo = GetConsumableTypeInfo(benchmark.BenchmarkCase.Descriptor.IterationSetupMethod?.ReturnType);
iterationCleanupReturnInfo = GetConsumableTypeInfo(benchmark.BenchmarkCase.Descriptor.IterationCleanupMethod?.ReturnType);
- // Init types
+ // Init type
runnableBuilder = DefineRunnableTypeBuilder(benchmark, moduleBuilder);
- overheadDelegateType = EmitOverheadDelegateType();
- workloadDelegateType = EmitWorkloadDelegateType();
}
private static ConsumableTypeInfo GetConsumableTypeInfo(Type methodReturnType)
@@ -378,52 +365,6 @@ private static ConsumableTypeInfo GetConsumableTypeInfo(Type methodReturnType)
return methodReturnType == null ? null : new ConsumableTypeInfo(methodReturnType);
}
- private Type EmitOverheadDelegateType()
- {
- // .class public auto ansi sealed BenchmarkDotNet.Autogenerated.Runnable_0OverheadDelegate
- // extends[mscorlib]System.MulticastDelegate;
- var overheadReturnType = EmitParameterInfo.CreateReturnParameter(consumableInfo.OverheadMethodReturnType);
-
- // replace arg names
- var overheadParameters = Descriptor.WorkloadMethod.GetParameters()
- .Select(p =>
- (ParameterInfo)new EmitParameterInfo(
- p.Position,
- ArgParamPrefix + p.Position,
- p.ParameterType,
- p.Attributes,
- null))
- .ToArray();
-
- return moduleBuilder.EmitCustomDelegate(
- GetRunnableTypeName(benchmark) + OverheadDelegateTypeSuffix,
- overheadReturnType,
- overheadParameters);
- }
-
- private Type EmitWorkloadDelegateType()
- {
- // .class public auto ansi sealed BenchmarkDotNet.Autogenerated.Runnable_0WorkloadDelegate
- // extends [mscorlib]System.MulticastDelegate
- var workloadReturnType = EmitParameterInfo.CreateReturnParameter(consumableInfo.WorkloadMethodReturnType);
-
- // Replace arg names
- var workloadParameters = Descriptor.WorkloadMethod.GetParameters()
- .Select(p =>
- (ParameterInfo)new EmitParameterInfo(
- p.Position,
- ArgParamPrefix + p.Position,
- p.ParameterType,
- p.Attributes,
- null))
- .ToArray();
-
- return moduleBuilder.EmitCustomDelegate(
- GetRunnableTypeName(benchmark) + WorkloadDelegateTypeSuffix,
- workloadReturnType,
- workloadParameters);
- }
-
private void DefineFields()
{
globalSetupActionField =
@@ -434,10 +375,6 @@ private void DefineFields()
runnableBuilder.DefineField(IterationSetupActionFieldName, typeof(Action), FieldAttributes.Private);
iterationCleanupActionField =
runnableBuilder.DefineField(IterationCleanupActionFieldName, typeof(Action), FieldAttributes.Private);
- overheadDelegateField =
- runnableBuilder.DefineField(OverheadDelegateFieldName, overheadDelegateType, FieldAttributes.Private);
- workloadDelegateField =
- runnableBuilder.DefineField(WorkloadDelegateFieldName, workloadDelegateType, FieldAttributes.Private);
// Define arg fields
foreach (var parameter in Descriptor.WorkloadMethod.GetParameters())
@@ -485,7 +422,6 @@ private void DefineFields()
notElevenField = runnableBuilder.DefineField(NotElevenFieldName, typeof(int), FieldAttributes.Public);
dummyVarField = runnableBuilder.DefineField(DummyVarFieldName, typeof(int), FieldAttributes.Private);
- consumeEmitter.OnDefineFields(runnableBuilder);
}
private ConstructorBuilder DefineCtor()
@@ -553,166 +489,117 @@ private MethodBuilder EmitDummyMethod(string methodName, int unrollFactor)
private MethodBuilder EmitOverheadImplementation(string methodName)
{
- var overheadInvokeMethod = TypeBuilderExtensions.GetDelegateInvokeMethod(overheadDelegateType);
-
//.method private hidebysig
- // instance int32 __Overhead(int64 arg0) cil managed
+ // instance void __Overhead(int64 arg0) cil managed
+
+ // Replace arg names
+ var parameters = Descriptor.WorkloadMethod.GetParameters()
+ .Select(p =>
+ (ParameterInfo) new EmitParameterInfo(
+ p.Position,
+ ArgParamPrefix + p.Position,
+ p.ParameterType,
+ p.Attributes,
+ null))
+ .ToArray();
+
var methodBuilder = runnableBuilder.DefineNonVirtualInstanceMethod(
methodName,
MethodAttributes.Private,
- overheadInvokeMethod.ReturnParameter,
- overheadInvokeMethod.GetParameters());
+ EmitParameterInfo.CreateReturnVoidParameter(),
+ parameters)
+ .SetNoInliningImplementationFlag();
var ilBuilder = methodBuilder.GetILGenerator();
- var returnType = methodBuilder.ReturnType;
-
/*
- // return default;
- IL_0000: ldc.i4.0
+ // return;
IL_0001: ret
*/
- // optional local if default(T) uses .initobj
- var optionalLocalForInitobj = ilBuilder.DeclareOptionalLocalForReturnDefault(returnType);
- ilBuilder.EmitReturnDefault(returnType, optionalLocalForInitobj);
+ ilBuilder.EmitVoidReturn(methodBuilder);
return methodBuilder;
}
- private MethodInfo EmitWorkloadImplementation(string methodName)
- {
- // Shortcut: DO NOT emit method if the result type is not awaitable
- if (!consumableInfo.IsAwaitable)
- return Descriptor.WorkloadMethod;
-
- var workloadInvokeMethod = TypeBuilderExtensions.GetDelegateInvokeMethod(workloadDelegateType);
-
- //.method private hidebysig
- // instance int32 __Workload(int64 arg0) cil managed
- var args = workloadInvokeMethod.GetParameters();
- var methodBuilder = runnableBuilder.DefineNonVirtualInstanceMethod(
- methodName,
- MethodAttributes.Private,
- workloadInvokeMethod.ReturnParameter,
- args);
- args = methodBuilder.GetEmitParameters(args);
-
- var ilBuilder = methodBuilder.GetILGenerator();
-
- /*
- IL_0026: ldarg.0
- IL_0027: ldloc.0
- IL_0028: ldloc.1
- IL_0029: ldloc.2
- IL_002a: ldloc.3
- IL_002b: call instance class [System.Private.CoreLib]System.Threading.Tasks.Task`1