-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Removed JIT (uint) cast workaround to elide bound-checks in CoreLib #67448
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
Changes from 4 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) | ||||
| (int bitArrayIndex, int position) = Math.DivRem(bitPosition, IntSize); | ||||
|
||||
| if ((uint)bitArrayIndex < (uint)span.Length) |
asm (x64) current main
; Assembly listing for method System.Collections.Generic.BitHelper:IsMarked(int):bool:this
; Emitting BLENDED_CODE for X64 CPU with SSE2 - Windows
; ReadyToRun compilation
; optimized code
; rsp based frame
; partially interruptible
; No PGO data
; 0 inlinees with PGO data; 1 single block inlinees; 0 inlinees without PGO data
; Final local variable assignments
;
; V00 this [V00,T01] ( 4, 3.50) byref -> rcx this single-def
; V01 arg1 [V01,T00] ( 7, 5.50) int -> rdx single-def
; V02 loc0 [V02,T02] ( 3, 2.50) int -> rax
;# V03 OutArgs [V03 ] ( 1, 1 ) lclBlk ( 0) [rsp+00H] "OutgoingArgSpace"
;* V04 tmp1 [V04 ] ( 0, 0 ) byref -> zero-ref single-def "Span.get_Item ptrToSpan"
;* V05 tmp2 [V05 ] ( 0, 0 ) byref -> zero-ref "Inlining Arg"
; V06 cse0 [V06,T03] ( 2, 2 ) int -> r8 "CSE - aggressive"
;
; Lcl frame size = 0
G_M54322_IG01:
;; size=0 bbWeight=0.50 PerfScore 0.00
G_M54322_IG02:
mov eax, edx
sar eax, 31
and eax, 31
add eax, edx
sar eax, 5
mov r8d, dword ptr [rcx+8]
cmp eax, r8d
jae SHORT G_M54322_IG05
;; size=22 bbWeight=1 PerfScore 5.00
G_M54322_IG03:
mov rcx, bword ptr [rcx]
mov eax, eax
mov eax, dword ptr [rcx+4*rax]
mov ecx, edx
sar ecx, 31
and ecx, 31
add ecx, edx
and ecx, -32
sub edx, ecx
bt eax, edx
setb al
movzx rax, al
;; size=32 bbWeight=0.50 PerfScore 3.88
G_M54322_IG04:
ret
;; size=1 bbWeight=0.50 PerfScore 0.50
G_M54322_IG05:
xor eax, eax
;; size=2 bbWeight=0.50 PerfScore 0.12
G_M54322_IG06:
ret
;; size=1 bbWeight=0.50 PerfScore 0.50
; Total bytes of code 58, prolog size 0, PerfScore 15.80, instruction count 23, allocated bytes for code 58 (MethodHash=300a2bcd) for method System.Collections.Generic.BitHelper:IsMarked(int):bool:this
; ============================================================
; Assembly listing for method System.Collections.Generic.BitHelper:MarkBit(int):this
; Emitting BLENDED_CODE for X64 CPU with SSE2 - Windows
; ReadyToRun compilation
; optimized code
; rsp based frame
; partially interruptible
; No PGO data
; 0 inlinees with PGO data; 1 single block inlinees; 0 inlinees without PGO data
; Final local variable assignments
;
; V00 this [V00,T01] ( 4, 3.50) byref -> rcx this single-def
; V01 arg1 [V01,T00] ( 7, 5.50) int -> rdx single-def
; V02 loc0 [V02,T03] ( 3, 2.50) int -> rax
;# V03 OutArgs [V03 ] ( 1, 1 ) lclBlk ( 0) [rsp+00H] "OutgoingArgSpace"
;* V04 tmp1 [V04 ] ( 0, 0 ) byref -> zero-ref single-def "Span.get_Item ptrToSpan"
; V05 tmp2 [V05,T02] ( 3, 3 ) byref -> rax single-def "dup spill"
;* V06 tmp3 [V06 ] ( 0, 0 ) byref -> zero-ref "Inlining Arg"
; V07 cse0 [V07,T04] ( 2, 2 ) int -> r8 "CSE - aggressive"
;
; Lcl frame size = 0
G_M49794_IG01:
;; size=0 bbWeight=1 PerfScore 0.00
G_M49794_IG02:
mov eax, edx
sar eax, 31
and eax, 31
add eax, edx
sar eax, 5
mov r8d, dword ptr [rcx+8]
cmp eax, r8d
jae SHORT G_M49794_IG04
;; size=22 bbWeight=1 PerfScore 5.00
G_M49794_IG03:
mov rcx, bword ptr [rcx]
mov eax, eax
lea rax, bword ptr [rcx+4*rax]
mov ecx, edx
sar ecx, 31
and ecx, 31
add ecx, edx
and ecx, -32
sub edx, ecx
mov r8d, 1
mov ecx, edx
shl r8d, cl
or dword ptr [rax], r8d
;; size=38 bbWeight=0.50 PerfScore 5.00
G_M49794_IG04:
ret
;; size=1 bbWeight=1 PerfScore 1.00
; Total bytes of code 61, prolog size 0, PerfScore 17.10, instruction count 22, allocated bytes for code 61 (MethodHash=de493d7d) for method System.Collections.Generic.BitHelper:MarkBit(int):this
; ============================================================asm (x64) PR
; Assembly listing for method System.Collections.Generic.BitHelper:IsMarked(int):bool:this
; Emitting BLENDED_CODE for X64 CPU with SSE2 - Windows
; ReadyToRun compilation
; optimized code
; rsp based frame
; partially interruptible
; No PGO data
; 0 inlinees with PGO data; 3 single block inlinees; 0 inlinees without PGO data
; Final local variable assignments
;
; V00 this [V00,T01] ( 4, 4 ) byref -> rcx this single-def
; V01 arg1 [V01,T00] ( 5, 5 ) int -> rdx single-def
; V02 loc0 [V02,T03] ( 3, 2.50) int -> rax
; V03 loc1 [V03,T07] ( 2, 1.50) int -> rdx
;* V04 loc2 [V04 ] ( 0, 0 ) struct (16) zero-ref ld-addr-op
;# V05 OutArgs [V05 ] ( 1, 1 ) lclBlk ( 0) [rsp+00H] "OutgoingArgSpace"
;* V06 tmp1 [V06 ] ( 0, 0 ) struct ( 8) zero-ref "dup spill"
; V07 tmp2 [V07,T02] ( 3, 3 ) int -> rax "Inline stloc first use temp"
;* V08 tmp3 [V08 ] ( 0, 0 ) struct ( 8) zero-ref "NewObj constructor temp"
;* V09 tmp4 [V09 ] ( 0, 0 ) int -> zero-ref "Inlining Arg"
; V10 tmp5 [V10,T06] ( 2, 1.50) byref -> r8 single-def V04._pointer(offs=0x00) P-INDEP "field V04._pointer (fldOffset=0x0)"
; V11 tmp6 [V11,T04] ( 2, 2 ) int -> rcx V04._length(offs=0x08) P-INDEP "field V04._length (fldOffset=0x8)"
;* V12 tmp7 [V12 ] ( 0, 0 ) int -> zero-ref V06.Item1(offs=0x00) P-INDEP "field V06.Item1 (fldOffset=0x0)"
;* V13 tmp8 [V13 ] ( 0, 0 ) int -> zero-ref V06.Item2(offs=0x04) P-INDEP "field V06.Item2 (fldOffset=0x4)"
;* V14 tmp9 [V14 ] ( 0, 0 ) int -> zero-ref V08.Item1(offs=0x00) P-INDEP "field V08.Item1 (fldOffset=0x0)"
; V15 tmp10 [V15,T05] ( 2, 2 ) int -> rdx V08.Item2(offs=0x04) P-INDEP "field V08.Item2 (fldOffset=0x4)"
;
; Lcl frame size = 0
G_M54322_IG01:
;; size=0 bbWeight=0.50 PerfScore 0.00
G_M54322_IG02:
mov eax, edx
sar eax, 31
and eax, 31
add eax, edx
sar eax, 5
mov r8d, eax
shl r8d, 5
sub edx, r8d
mov r8, bword ptr [rcx]
mov ecx, dword ptr [rcx+8]
cmp eax, ecx
jae SHORT G_M54322_IG05
;; size=33 bbWeight=1 PerfScore 8.00
G_M54322_IG03:
mov eax, eax
mov eax, dword ptr [r8+4*rax]
bt eax, edx
setb al
movzx rax, al
;; size=15 bbWeight=0.50 PerfScore 2.00
G_M54322_IG04:
ret
;; size=1 bbWeight=0.50 PerfScore 0.50
G_M54322_IG05:
xor eax, eax
;; size=2 bbWeight=0.50 PerfScore 0.12
G_M54322_IG06:
ret
;; size=1 bbWeight=0.50 PerfScore 0.50
; Total bytes of code 52, prolog size 0, PerfScore 16.33, instruction count 20, allocated bytes for code 52 (MethodHash=300a2bcd) for method System.Collections.Generic.BitHelper:IsMarked(int):bool:this
; ============================================================
; Assembly listing for method System.Collections.Generic.BitHelper:MarkBit(int):this
; Emitting BLENDED_CODE for X64 CPU with SSE2 - Windows
; ReadyToRun compilation
; optimized code
; rsp based frame
; partially interruptible
; No PGO data
; 0 inlinees with PGO data; 3 single block inlinees; 0 inlinees without PGO data
; Final local variable assignments
;
; V00 this [V00,T01] ( 4, 4 ) byref -> rcx this single-def
; V01 arg1 [V01,T00] ( 5, 5 ) int -> rdx single-def
; V02 loc0 [V02,T04] ( 3, 2.50) int -> rax
; V03 loc1 [V03,T08] ( 2, 1.50) int -> rdx
;* V04 loc2 [V04 ] ( 0, 0 ) struct (16) zero-ref ld-addr-op
;# V05 OutArgs [V05 ] ( 1, 1 ) lclBlk ( 0) [rsp+00H] "OutgoingArgSpace"
;* V06 tmp1 [V06 ] ( 0, 0 ) struct ( 8) zero-ref "dup spill"
; V07 tmp2 [V07,T02] ( 3, 3 ) byref -> rax single-def "dup spill"
; V08 tmp3 [V08,T03] ( 3, 3 ) int -> rax "Inline stloc first use temp"
;* V09 tmp4 [V09 ] ( 0, 0 ) struct ( 8) zero-ref "NewObj constructor temp"
;* V10 tmp5 [V10 ] ( 0, 0 ) int -> zero-ref "Inlining Arg"
; V11 tmp6 [V11,T07] ( 2, 1.50) byref -> r8 single-def V04._pointer(offs=0x00) P-INDEP "field V04._pointer (fldOffset=0x0)"
; V12 tmp7 [V12,T05] ( 2, 2 ) int -> rcx V04._length(offs=0x08) P-INDEP "field V04._length (fldOffset=0x8)"
;* V13 tmp8 [V13 ] ( 0, 0 ) int -> zero-ref V06.Item1(offs=0x00) P-INDEP "field V06.Item1 (fldOffset=0x0)"
;* V14 tmp9 [V14 ] ( 0, 0 ) int -> zero-ref V06.Item2(offs=0x04) P-INDEP "field V06.Item2 (fldOffset=0x4)"
;* V15 tmp10 [V15 ] ( 0, 0 ) int -> zero-ref V09.Item1(offs=0x00) P-INDEP "field V09.Item1 (fldOffset=0x0)"
; V16 tmp11 [V16,T06] ( 2, 2 ) int -> rdx V09.Item2(offs=0x04) P-INDEP "field V09.Item2 (fldOffset=0x4)"
;
; Lcl frame size = 0
G_M49794_IG01:
;; size=0 bbWeight=1 PerfScore 0.00
G_M49794_IG02:
mov eax, edx
sar eax, 31
and eax, 31
add eax, edx
sar eax, 5
mov r8d, eax
shl r8d, 5
sub edx, r8d
mov r8, bword ptr [rcx]
mov ecx, dword ptr [rcx+8]
cmp eax, ecx
jae SHORT G_M49794_IG04
;; size=33 bbWeight=1 PerfScore 8.00
G_M49794_IG03:
mov ecx, eax
lea rax, bword ptr [r8+4*rcx]
mov r8d, 1
mov ecx, edx
shl r8d, cl
or dword ptr [rax], r8d
;; size=20 bbWeight=0.50 PerfScore 3.12
G_M49794_IG04:
ret
;; size=1 bbWeight=1 PerfScore 1.00
; Total bytes of code 54, prolog size 0, PerfScore 17.53, instruction count 19, allocated bytes for code 54 (MethodHash=de493d7d) for method System.Collections.Generic.BitHelper:MarkBit(int):this
; ============================================================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.
This division does not need to be signed. The input is always non-negative. Would it be better to cast the input to uint before the division? Then it is going to be just a single shift and a single mask without the additional instructions to get the sign right.
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.
Good point 👍🏻. The divisor is constant too, so that becomes a cheap operation -- like you said.
(Will update later, tomorrow -- being on mobile now).
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.
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 comment
The reason will be displayed to describe this comment to others. Learn more.
Filed #72004 for this.
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -170,9 +170,10 @@ public void Insert(int index, string? s) | |||||
| public void Append(char c) | ||||||
| { | ||||||
| int pos = _pos; | ||||||
| if ((uint)pos < (uint)_chars.Length) | ||||||
| Span<char> chars = _chars; | ||||||
|
||||||
| if (number.IsNegative && (section == 0) && (number.Scale != 0)) | |
| sb.Append(info.NegativeSign); |
ValueStringBuilder is used in that method.
I guess
Extra aggresive inlined struct local may push us over JIT optimization limits.
is the case here.
In conclusion I think it's the best to revert the change in ValueStringBuilder, and fix the JIT for
I think the JIT missed the optimization here, especially as it's a
ref structthus_charscan't be changed in between the length-check and the index-access.
so we get the best of both worlds.
If JIT is fixed we should get the improvements in the jit-diff anyway without regressing System.Number:NumberToStringFormat.
Hm, I don't find any issue for this missed optimization, but believe to remember that this was already mentioned somewhere else.
| 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) | ||
|
Member
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.
Member
Author
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.
Member
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); | ||
| } | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
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 == 0and now will douint.MaxValue / 32 = 134217727There 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
uinttouint)