Skip to content

Commit a71586f

Browse files
benaadamsdotnet-bot
authored andcommitted
Support faster null checks (dotnet/coreclr#21765)
Signed-off-by: dotnet-bot <[email protected]>
1 parent 4f3baf9 commit a71586f

File tree

11 files changed

+134
-96
lines changed

11 files changed

+134
-96
lines changed

src/Common/src/CoreLib/System/Globalization/SortVersion.cs

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for more information.
44

5+
using System.Runtime.CompilerServices;
6+
57
namespace System.Globalization
68
{
79
[Serializable]
@@ -75,20 +77,19 @@ public override int GetHashCode()
7577
return m_NlsVersion * 7 | m_SortId.GetHashCode();
7678
}
7779

80+
// Force inline as the true/false ternary takes it above ALWAYS_INLINE size even though the asm ends up smaller
81+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
7882
public static bool operator ==(SortVersion left, SortVersion right)
7983
{
80-
if (((object)left) != null)
81-
{
82-
return left.Equals(right);
83-
}
84-
85-
if (((object)right) != null)
84+
// Test "right" first to allow branch elimination when inlined for null checks (== null)
85+
// so it can become a simple test
86+
if (right is null)
8687
{
87-
return right.Equals(left);
88+
// return true/false not the test result https://github.com/dotnet/coreclr/issues/914
89+
return (left is null) ? true : false;
8890
}
8991

90-
// Both null.
91-
return true;
92+
return right.Equals(left);
9293
}
9394

9495
public static bool operator !=(SortVersion left, SortVersion right)

src/Common/src/CoreLib/System/Reflection/Assembly.cs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using System.Configuration.Assemblies;
99
using System.Runtime.Serialization;
1010
using System.Security;
11+
using System.Runtime.CompilerServices;
1112

1213
namespace System.Reflection
1314
{
@@ -147,15 +148,20 @@ public override string ToString()
147148
public override bool Equals(object o) => base.Equals(o);
148149
public override int GetHashCode() => base.GetHashCode();
149150

151+
// Force inline as the true/false ternary takes it above ALWAYS_INLINE size even though the asm ends up smaller
152+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
150153
public static bool operator ==(Assembly left, Assembly right)
151154
{
152-
if (object.ReferenceEquals(left, right))
153-
return true;
154-
155-
if ((object)left == null || (object)right == null)
156-
return false;
155+
// Test "right" first to allow branch elimination when inlined for null checks (== null)
156+
// so it can become a simple test
157+
if (right is null)
158+
{
159+
// return true/false not the test result https://github.com/dotnet/coreclr/issues/914
160+
return (left is null) ? true : false;
161+
}
157162

158-
return left.Equals(right);
163+
// Quick reference equality test prior to calling the virtual Equality
164+
return ReferenceEquals(right, left) ? true : right.Equals(left);
159165
}
160166

161167
public static bool operator !=(Assembly left, Assembly right)

src/Common/src/CoreLib/System/Reflection/ConstructorInfo.cs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
using System.Diagnostics;
66
using System.Globalization;
7+
using System.Runtime.CompilerServices;
78

89
namespace System.Reflection
910
{
@@ -21,15 +22,20 @@ protected ConstructorInfo() { }
2122
public override bool Equals(object obj) => base.Equals(obj);
2223
public override int GetHashCode() => base.GetHashCode();
2324

25+
// Force inline as the true/false ternary takes it above ALWAYS_INLINE size even though the asm ends up smaller
26+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2427
public static bool operator ==(ConstructorInfo left, ConstructorInfo right)
2528
{
26-
if (object.ReferenceEquals(left, right))
27-
return true;
28-
29-
if ((object)left == null || (object)right == null)
30-
return false;
31-
32-
return left.Equals(right);
29+
// Test "right" first to allow branch elimination when inlined for null checks (== null)
30+
// so it can become a simple test
31+
if (right is null)
32+
{
33+
// return true/false not the test result https://github.com/dotnet/coreclr/issues/914
34+
return (left is null) ? true : false;
35+
}
36+
37+
// Quick reference equality test prior to calling the virtual Equality
38+
return ReferenceEquals(right, left) ? true : right.Equals(left);
3339
}
3440

3541
public static bool operator !=(ConstructorInfo left, ConstructorInfo right) => !(left == right);

src/Common/src/CoreLib/System/Reflection/EventInfo.cs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// See the LICENSE file in the project root for more information.
44

55
using System.Diagnostics;
6+
using System.Runtime.CompilerServices;
67

78
#if FEATURE_COMINTEROP
89
using EventRegistrationToken = System.Runtime.InteropServices.WindowsRuntime.EventRegistrationToken;
@@ -99,15 +100,20 @@ public virtual void RemoveEventHandler(object target, Delegate handler)
99100
public override bool Equals(object obj) => base.Equals(obj);
100101
public override int GetHashCode() => base.GetHashCode();
101102

103+
// Force inline as the true/false ternary takes it above ALWAYS_INLINE size even though the asm ends up smaller
104+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
102105
public static bool operator ==(EventInfo left, EventInfo right)
103106
{
104-
if (object.ReferenceEquals(left, right))
105-
return true;
106-
107-
if ((object)left == null || (object)right == null)
108-
return false;
107+
// Test "right" first to allow branch elimination when inlined for null checks (== null)
108+
// so it can become a simple test
109+
if (right is null)
110+
{
111+
// return true/false not the test result https://github.com/dotnet/coreclr/issues/914
112+
return (left is null) ? true : false;
113+
}
109114

110-
return left.Equals(right);
115+
// Quick reference equality test prior to calling the virtual Equality
116+
return ReferenceEquals(right, left) ? true : right.Equals(left);
111117
}
112118

113119
public static bool operator !=(EventInfo left, EventInfo right) => !(left == right);

src/Common/src/CoreLib/System/Reflection/FieldInfo.cs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
using System.Diagnostics;
66
using System.Globalization;
7+
using System.Runtime.CompilerServices;
78

89
namespace System.Reflection
910
{
@@ -39,15 +40,20 @@ protected FieldInfo() { }
3940
public override bool Equals(object obj) => base.Equals(obj);
4041
public override int GetHashCode() => base.GetHashCode();
4142

43+
// Force inline as the true/false ternary takes it above ALWAYS_INLINE size even though the asm ends up smaller
44+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
4245
public static bool operator ==(FieldInfo left, FieldInfo right)
4346
{
44-
if (object.ReferenceEquals(left, right))
45-
return true;
46-
47-
if ((object)left == null || (object)right == null)
48-
return false;
49-
50-
return left.Equals(right);
47+
// Test "right" first to allow branch elimination when inlined for null checks (== null)
48+
// so it can become a simple test
49+
if (right is null)
50+
{
51+
// return true/false not the test result https://github.com/dotnet/coreclr/issues/914
52+
return (left is null) ? true : false;
53+
}
54+
55+
// Quick reference equality test prior to calling the virtual Equality
56+
return ReferenceEquals(right, left) ? true : right.Equals(left);
5157
}
5258

5359
public static bool operator !=(FieldInfo left, FieldInfo right) => !(left == right);

src/Common/src/CoreLib/System/Reflection/MemberInfo.cs

Lines changed: 12 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// See the LICENSE file in the project root for more information.
44

55
using System.Collections.Generic;
6+
using System.Runtime.CompilerServices;
67

78
namespace System.Reflection
89
{
@@ -44,32 +45,20 @@ public virtual Module Module
4445
public override bool Equals(object obj) => base.Equals(obj);
4546
public override int GetHashCode() => base.GetHashCode();
4647

48+
// Force inline as the true/false ternary takes it above ALWAYS_INLINE size even though the asm ends up smaller
49+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
4750
public static bool operator ==(MemberInfo left, MemberInfo right)
4851
{
49-
if (object.ReferenceEquals(left, right))
50-
return true;
51-
52-
if ((object)left == null || (object)right == null)
53-
return false;
54-
55-
Type type1, type2;
56-
MethodBase method1, method2;
57-
FieldInfo field1, field2;
58-
EventInfo event1, event2;
59-
PropertyInfo property1, property2;
60-
61-
if ((type1 = left as Type) != null && (type2 = right as Type) != null)
62-
return type1 == type2;
63-
else if ((method1 = left as MethodBase) != null && (method2 = right as MethodBase) != null)
64-
return method1 == method2;
65-
else if ((field1 = left as FieldInfo) != null && (field2 = right as FieldInfo) != null)
66-
return field1 == field2;
67-
else if ((event1 = left as EventInfo) != null && (event2 = right as EventInfo) != null)
68-
return event1 == event2;
69-
else if ((property1 = left as PropertyInfo) != null && (property2 = right as PropertyInfo) != null)
70-
return property1 == property2;
52+
// Test "right" first to allow branch elimination when inlined for null checks (== null)
53+
// so it can become a simple test
54+
if (right is null)
55+
{
56+
// return true/false not the test result https://github.com/dotnet/coreclr/issues/914
57+
return (left is null) ? true : false;
58+
}
7159

72-
return false;
60+
// Quick reference equality test prior to calling the virtual Equality
61+
return ReferenceEquals(right, left) ? true : right.Equals(left);
7362
}
7463

7564
public static bool operator !=(MemberInfo left, MemberInfo right) => !(left == right);

src/Common/src/CoreLib/System/Reflection/MethodBase.cs

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
using System.Globalization;
66
using System.Diagnostics;
7+
using System.Runtime.CompilerServices;
78

89
namespace System.Reflection
910
{
@@ -62,23 +63,20 @@ public bool IsConstructor
6263
public override bool Equals(object obj) => base.Equals(obj);
6364
public override int GetHashCode() => base.GetHashCode();
6465

66+
// Force inline as the true/false ternary takes it above ALWAYS_INLINE size even though the asm ends up smaller
67+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
6568
public static bool operator ==(MethodBase left, MethodBase right)
6669
{
67-
if (object.ReferenceEquals(left, right))
68-
return true;
69-
70-
if ((object)left == null || (object)right == null)
71-
return false;
72-
73-
MethodInfo method1, method2;
74-
ConstructorInfo constructor1, constructor2;
75-
76-
if ((method1 = left as MethodInfo) != null && (method2 = right as MethodInfo) != null)
77-
return method1 == method2;
78-
else if ((constructor1 = left as ConstructorInfo) != null && (constructor2 = right as ConstructorInfo) != null)
79-
return constructor1 == constructor2;
70+
// Test "right" first to allow branch elimination when inlined for null checks (== null)
71+
// so it can become a simple test
72+
if (right is null)
73+
{
74+
// return true/false not the test result https://github.com/dotnet/coreclr/issues/914
75+
return (left is null) ? true : false;
76+
}
8077

81-
return false;
78+
// Quick reference equality test prior to calling the virtual Equality
79+
return ReferenceEquals(right, left) ? true : right.Equals(left);
8280
}
8381

8482
public static bool operator !=(MethodBase left, MethodBase right) => !(left == right);

src/Common/src/CoreLib/System/Reflection/MethodInfo.cs

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for more information.
44

5+
using System.Runtime.CompilerServices;
6+
57
namespace System.Reflection
68
{
79
public abstract partial class MethodInfo : MethodBase
@@ -27,15 +29,20 @@ protected MethodInfo() { }
2729
public override bool Equals(object obj) => base.Equals(obj);
2830
public override int GetHashCode() => base.GetHashCode();
2931

32+
// Force inline as the true/false ternary takes it above ALWAYS_INLINE size even though the asm ends up smaller
33+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
3034
public static bool operator ==(MethodInfo left, MethodInfo right)
3135
{
32-
if (object.ReferenceEquals(left, right))
33-
return true;
34-
35-
if ((object)left == null || (object)right == null)
36-
return false;
37-
38-
return left.Equals(right);
36+
// Test "right" first to allow branch elimination when inlined for null checks (== null)
37+
// so it can become a simple test
38+
if (right is null)
39+
{
40+
// return true/false not the test result https://github.com/dotnet/coreclr/issues/914
41+
return (left is null) ? true : false;
42+
}
43+
44+
// Quick reference equality test prior to calling the virtual Equality
45+
return ReferenceEquals(right, left) ? true : right.Equals(left);
3946
}
4047

4148
public static bool operator !=(MethodInfo left, MethodInfo right) => !(left == right);

src/Common/src/CoreLib/System/Reflection/Module.cs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// See the LICENSE file in the project root for more information.
44

55
using System.Collections.Generic;
6+
using System.Runtime.CompilerServices;
67
using System.Runtime.Serialization;
78

89
namespace System.Reflection
@@ -114,16 +115,21 @@ public virtual Type[] FindTypes(TypeFilter filter, object filterCriteria)
114115

115116
public override bool Equals(object o) => base.Equals(o);
116117
public override int GetHashCode() => base.GetHashCode();
117-
118+
119+
// Force inline as the true/false ternary takes it above ALWAYS_INLINE size even though the asm ends up smaller
120+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
118121
public static bool operator ==(Module left, Module right)
119122
{
120-
if (object.ReferenceEquals(left, right))
121-
return true;
122-
123-
if ((object)left == null || (object)right == null)
124-
return false;
123+
// Test "right" first to allow branch elimination when inlined for null checks (== null)
124+
// so it can become a simple test
125+
if (right is null)
126+
{
127+
// return true/false not the test result https://github.com/dotnet/coreclr/issues/914
128+
return (left is null) ? true : false;
129+
}
125130

126-
return left.Equals(right);
131+
// Quick reference equality test prior to calling the virtual Equality
132+
return ReferenceEquals(right, left) ? true : right.Equals(left);
127133
}
128134

129135
public static bool operator !=(Module left, Module right) => !(left == right);

src/Common/src/CoreLib/System/Reflection/PropertyInfo.cs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
using System.Diagnostics;
66
using System.Globalization;
7+
using System.Runtime.CompilerServices;
78

89
namespace System.Reflection
910
{
@@ -58,15 +59,20 @@ protected PropertyInfo() { }
5859
public override bool Equals(object obj) => base.Equals(obj);
5960
public override int GetHashCode() => base.GetHashCode();
6061

62+
// Force inline as the true/false ternary takes it above ALWAYS_INLINE size even though the asm ends up smaller
63+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
6164
public static bool operator ==(PropertyInfo left, PropertyInfo right)
6265
{
63-
if (object.ReferenceEquals(left, right))
64-
return true;
65-
66-
if ((object)left == null || (object)right == null)
67-
return false;
68-
69-
return left.Equals(right);
66+
// Test "right" first to allow branch elimination when inlined for null checks (== null)
67+
// so it can become a simple test
68+
if (right is null)
69+
{
70+
// return true/false not the test result https://github.com/dotnet/coreclr/issues/914
71+
return (left is null) ? true : false;
72+
}
73+
74+
// Quick reference equality test prior to calling the virtual Equality
75+
return ReferenceEquals(right, left) ? true : right.Equals(left);
7076
}
7177

7278
public static bool operator !=(PropertyInfo left, PropertyInfo right) => !(left == right);

0 commit comments

Comments
 (0)