Skip to content

Commit 40a1c2b

Browse files
committed
Only use default for types that cannot be passed to generic function (ByRefLike and pointers), use Unsafe.SkipInit for everything else.
Check for pointer type when setting default local in InProcessEmitToolchain.
1 parent fbdb74c commit 40a1c2b

File tree

4 files changed

+8
-14
lines changed

4 files changed

+8
-14
lines changed

src/BenchmarkDotNet/Code/DeclarationsProvider.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,8 @@ public override string OverheadImplementation
9090
get
9191
{
9292
var type = WorkloadMethodReturnType;
93-
bool isByRefLike = type.IsByRefLike();
94-
if (isByRefLike || (Consumer.IsConsumable(type) && !isByRefLike))
93+
// ByRefLike types and pointers use default, everything else uses Unsafe.SkipInit.
94+
if (type.IsByRefLike() || type.IsPointer)
9595
{
9696
return $"return default({type.GetCorrectCSharpTypeName()});";
9797
}

src/BenchmarkDotNet/Engines/Consumer.cs

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,6 @@ namespace BenchmarkDotNet.Engines
1212
{
1313
public class Consumer
1414
{
15-
private static readonly HashSet<Type> SupportedTypes
16-
= new HashSet<Type>(
17-
typeof(Consumer).GetTypeInfo()
18-
.DeclaredFields
19-
.Where(field => !field.IsStatic) // exclude this HashSet itself
20-
.Select(field => field.FieldType));
21-
2215
#pragma warning disable IDE0052 // Remove unread private members
2316
private volatile byte byteHolder;
2417
private volatile sbyte sbyteHolder;
@@ -130,9 +123,7 @@ public void Consume<T>(in T value)
130123
// This also works for empty structs, because the runtime enforces a minimum size of 1 byte.
131124
=> byteHolder = Unsafe.As<T, byte>(ref Unsafe.AsRef(in value));
132125

133-
internal static bool IsConsumable(Type type)
134-
// IsClass returns true for pointers.
135-
=> SupportedTypes.Contains(type) || type.GetTypeInfo().IsClass || type.GetTypeInfo().IsInterface || !type.IsByRefLike();
126+
internal static bool IsConsumable(Type type) => !type.IsByRefLike();
136127

137128
internal static bool HasConsumableField(Type type, out FieldInfo consumableField)
138129
{

src/BenchmarkDotNet/Helpers/Reflection.Emit/IlGeneratorDefaultValueExtensions.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ public static void EmitSetLocalToDefault(this ILGenerator ilBuilder, LocalBuilde
1919
{
2020
case Type t when t == typeof(void):
2121
break;
22+
case Type t when t.IsPointer: // Type.IsClass returns true for pointers, so we have to check for pointer type first.
23+
EmitInitObj(ilBuilder, resultType, local);
24+
break;
2225
case Type t when t.IsClass || t.IsInterface:
2326
ilBuilder.Emit(OpCodes.Ldnull);
2427
ilBuilder.EmitStloc(local);

src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/ConsumableConsumeEmitter.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,8 @@ protected override void OnEmitCtorBodyOverride(ConstructorBuilder constructorBui
8080

8181
public override void EmitOverheadImplementation(ILGenerator ilBuilder, Type returnType)
8282
{
83-
bool isByRefLike = returnType.IsByRefLike();
84-
if (isByRefLike || (Consumer.IsConsumable(returnType) && !isByRefLike))
83+
// ByRefLike types and pointers use default, everything else uses Unsafe.SkipInit.
84+
if (returnType.IsByRefLike() || returnType.IsPointer)
8585
{
8686
/*
8787
// return default;

0 commit comments

Comments
 (0)