-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Removed JIT (uint) cast workaround to elide bound-checks in CoreLib #67448
Changes from 6 commits
cf4ccd4
af9b2d6
19b8e00
33cd7a1
20842e6
da31c0b
7e816a5
d9f4e01
034dd89
76d6d2a
186fb58
6afa6c2
26f459f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,8 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System.Runtime.CompilerServices; | ||
|
||
namespace System.Collections.Generic | ||
{ | ||
internal ref struct BitHelper | ||
|
@@ -19,19 +21,22 @@ internal BitHelper(Span<int> span, bool clear) | |
|
||
internal void MarkBit(int bitPosition) | ||
{ | ||
int bitArrayIndex = bitPosition / IntSize; | ||
if ((uint)bitArrayIndex < (uint)_span.Length) | ||
(uint bitArrayIndex, uint position) = Math.DivRem((uint)bitPosition, IntSize); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is the code with DivRem as efficient as it would be if this used unsigned div and mod directly? DivRem is not treated as the intrinsic yet. My guess is that some unnecessary instructions compared to just using unsigned div and mod directly. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎉 this gives really nice codegen. Thanks! The jit-diff from this change allone (based on da31c0b to d9f4e01) shows good improvements.
I tried without the local Span, but then there were some regressions in larger methods. |
||
Span<int> span = _span; | ||
if (bitArrayIndex < (uint)span.Length) | ||
{ | ||
_span[bitArrayIndex] |= (1 << (bitPosition % IntSize)); | ||
span[(int)bitArrayIndex] |= 1 << (int)position; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The bound-check is still elided, even when casted back to |
||
} | ||
} | ||
|
||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
internal bool IsMarked(int bitPosition) | ||
{ | ||
int bitArrayIndex = bitPosition / IntSize; | ||
(uint bitArrayIndex, uint position) = Math.DivRem((uint)bitPosition, IntSize); | ||
Span<int> span = _span; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This one can use a tracking issue. The JIT should be smart enough to make it unnecessary to cache the Span here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Filed #72004 for this. |
||
return | ||
(uint)bitArrayIndex < (uint)_span.Length && | ||
(_span[bitArrayIndex] & (1 << (bitPosition % IntSize))) != 0; | ||
bitArrayIndex < (uint)span.Length && | ||
(span[(int)bitArrayIndex] & (1 << (int)position)) != 0; | ||
} | ||
|
||
/// <summary>How many ints must be allocated to represent n bits. Returns (n+31)/32, but avoids overflow.</summary> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -508,7 +508,7 @@ public static bool IsControl(string s, int index) | |
{ | ||
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); | ||
} | ||
if (((uint)index) >= ((uint)s.Length)) | ||
if ((uint)index >= (uint)s.Length) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These ones seem to be touching code "stylistically", we probably should exclude it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. W/o the superfluos paranthesis it's easier to read, thus I changed it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't disagree, but its also a "stylistic" change and so makes it harder to review the thing we actually care about. The typical PR policy is to not include such things and leave them to a separate PR, especially when its the only or primary change to a given file.
We should have issues tracking these. We really shouldn't be getting codegen differences between |
||
{ | ||
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index); | ||
} | ||
|
@@ -523,7 +523,7 @@ public static bool IsDigit(string s, int index) | |
{ | ||
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); | ||
} | ||
if (((uint)index) >= ((uint)s.Length)) | ||
if ((uint)index >= (uint)s.Length) | ||
{ | ||
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index); | ||
} | ||
|
@@ -543,7 +543,7 @@ public static bool IsLetter(string s, int index) | |
{ | ||
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); | ||
} | ||
if (((uint)index) >= ((uint)s.Length)) | ||
if ((uint)index >= (uint)s.Length) | ||
{ | ||
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index); | ||
} | ||
|
@@ -564,7 +564,7 @@ public static bool IsLetterOrDigit(string s, int index) | |
{ | ||
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); | ||
} | ||
if (((uint)index) >= ((uint)s.Length)) | ||
if ((uint)index >= (uint)s.Length) | ||
{ | ||
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index); | ||
} | ||
|
@@ -584,8 +584,7 @@ public static bool IsLower(string s, int index) | |
{ | ||
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); | ||
} | ||
|
||
if (((uint)index) >= ((uint)s.Length)) | ||
if ((uint)index >= (uint)s.Length) | ||
{ | ||
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index); | ||
} | ||
|
@@ -627,7 +626,7 @@ public static bool IsNumber(string s, int index) | |
{ | ||
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); | ||
} | ||
if (((uint)index) >= ((uint)s.Length)) | ||
if ((uint)index >= (uint)s.Length) | ||
{ | ||
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index); | ||
} | ||
|
@@ -660,7 +659,7 @@ public static bool IsPunctuation(string s, int index) | |
{ | ||
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); | ||
} | ||
if (((uint)index) >= ((uint)s.Length)) | ||
if ((uint)index >= (uint)s.Length) | ||
{ | ||
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index); | ||
} | ||
|
@@ -705,7 +704,7 @@ public static bool IsSeparator(string s, int index) | |
{ | ||
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); | ||
} | ||
if (((uint)index) >= ((uint)s.Length)) | ||
if ((uint)index >= (uint)s.Length) | ||
{ | ||
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index); | ||
} | ||
|
@@ -730,7 +729,7 @@ public static bool IsSurrogate(string s, int index) | |
{ | ||
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); | ||
} | ||
if (((uint)index) >= ((uint)s.Length)) | ||
if ((uint)index >= (uint)s.Length) | ||
{ | ||
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index); | ||
} | ||
|
@@ -762,7 +761,7 @@ public static bool IsSymbol(string s, int index) | |
{ | ||
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); | ||
} | ||
if (((uint)index) >= ((uint)s.Length)) | ||
if ((uint)index >= (uint)s.Length) | ||
{ | ||
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index); | ||
} | ||
|
@@ -782,7 +781,7 @@ public static bool IsUpper(string s, int index) | |
{ | ||
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); | ||
} | ||
if (((uint)index) >= ((uint)s.Length)) | ||
if ((uint)index >= (uint)s.Length) | ||
{ | ||
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index); | ||
} | ||
|
@@ -802,7 +801,7 @@ public static bool IsWhiteSpace(string s, int index) | |
{ | ||
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); | ||
} | ||
if (((uint)index) >= ((uint)s.Length)) | ||
if ((uint)index >= (uint)s.Length) | ||
{ | ||
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index); | ||
} | ||
|
@@ -828,7 +827,7 @@ public static UnicodeCategory GetUnicodeCategory(string s, int index) | |
{ | ||
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); | ||
} | ||
if (((uint)index) >= ((uint)s.Length)) | ||
if ((uint)index >= (uint)s.Length) | ||
{ | ||
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index); | ||
} | ||
|
@@ -852,7 +851,7 @@ public static double GetNumericValue(string s, int index) | |
{ | ||
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); | ||
} | ||
if (((uint)index) >= ((uint)s.Length)) | ||
if ((uint)index >= (uint)s.Length) | ||
{ | ||
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index); | ||
} | ||
|
@@ -874,7 +873,7 @@ public static bool IsHighSurrogate(string s, int index) | |
{ | ||
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); | ||
} | ||
if (((uint)index) >= ((uint)s.Length)) | ||
if ((uint)index >= (uint)s.Length) | ||
{ | ||
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index); | ||
} | ||
|
@@ -896,7 +895,7 @@ public static bool IsLowSurrogate(string s, int index) | |
{ | ||
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); | ||
} | ||
if (((uint)index) >= ((uint)s.Length)) | ||
if ((uint)index >= (uint)s.Length) | ||
{ | ||
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index); | ||
} | ||
|
@@ -913,7 +912,7 @@ public static bool IsSurrogatePair(string s, int index) | |
{ | ||
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); | ||
} | ||
if (((uint)index) >= ((uint)s.Length)) | ||
if ((uint)index >= (uint)s.Length) | ||
{ | ||
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we add a
Debug.Assert(bitPosition >= 0)
? Same for the other two methods here.The previous logic, would've done say
-1 / 32 == 0
and now will douint.MaxValue / 32 = 134217727
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might even be better to just keep this one "as is" since its really doing the same thing, just with more casts now (that is comparing
uint
touint
)