未验证 提交 6e33ec9a 编写于 作者: G Günther Foidl 提交者: GitHub

Removed JIT (uint) cast workaround to elide bound-checks in CoreLib (#67448)

* Removed (uint) cast workaround in CoreLib, annotated missing places with TODOs

* Fixed the bug I introduced in TimeSpanParse.TimeSpanTokenizer

* Use uint-cast on both sided for clarity

Cf. https://github.com/dotnet/runtime/pull/67448#discussion_r841082992

* Use uint-division in BitHelper as the JIT can produce more efficient code

Cf. https://github.com/dotnet/runtime/pull/67448#discussion_r841239729

* Don't have struct local in ValueStringBuilder due to hitting JIT optimization limits

Cf. https://github.com/dotnet/runtime/pull/67448#discussion_r841129465

* BitHelper with uint division and without Math.DivRem for even better codegen

AggressiveInlining isn't needed anymore, also the JIT recognizes the _span field, so no local Span is needed here.

* BitHelper needs local Span due to regression in jit-diff otherwise

* Added Debug.Asserts to BitHelper's method that take bitPosition

* BitHelper: comment for workaround to tracking issue
上级 608e3fd6
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics;
namespace System.Collections.Generic
{
internal ref struct BitHelper
......@@ -19,19 +21,29 @@ internal BitHelper(Span<int> span, bool clear)
internal void MarkBit(int bitPosition)
{
int bitArrayIndex = bitPosition / IntSize;
if ((uint)bitArrayIndex < (uint)_span.Length)
Debug.Assert(bitPosition >= 0);
uint bitArrayIndex = (uint)bitPosition / IntSize;
// Workaround for https://github.com/dotnet/runtime/issues/72004
Span<int> span = _span;
if (bitArrayIndex < (uint)span.Length)
{
_span[bitArrayIndex] |= (1 << (bitPosition % IntSize));
span[(int)bitArrayIndex] |= (1 << (int)((uint)bitPosition % IntSize));
}
}
internal bool IsMarked(int bitPosition)
{
int bitArrayIndex = bitPosition / IntSize;
Debug.Assert(bitPosition >= 0);
uint bitArrayIndex = (uint)bitPosition / IntSize;
// Workaround for https://github.com/dotnet/runtime/issues/72004
Span<int> span = _span;
return
(uint)bitArrayIndex < (uint)_span.Length &&
(_span[bitArrayIndex] & (1 << (bitPosition % IntSize))) != 0;
bitArrayIndex < (uint)span.Length &&
(span[(int)bitArrayIndex] & (1 << ((int)((uint)bitPosition % IntSize)))) != 0;
}
/// <summary>How many ints must be allocated to represent n bits. Returns (n+31)/32, but avoids overflow.</summary>
......
......@@ -99,7 +99,7 @@ public bool TryFormat(Span<char> destination, out int charsWritten)
{
if (m_value)
{
if ((uint)destination.Length > 3) // uint cast, per https://github.com/dotnet/runtime/issues/10596
if (destination.Length > 3)
{
ulong true_val = BitConverter.IsLittleEndian ? 0x65007500720054ul : 0x54007200750065ul; // "True"
MemoryMarshal.Write<ulong>(MemoryMarshal.AsBytes(destination), ref true_val);
......@@ -109,7 +109,7 @@ public bool TryFormat(Span<char> destination, out int charsWritten)
}
else
{
if ((uint)destination.Length > 4)
if (destination.Length > 4)
{
ulong fals_val = BitConverter.IsLittleEndian ? 0x73006C00610046ul : 0x460061006C0073ul; // "Fals"
MemoryMarshal.Write<ulong>(MemoryMarshal.AsBytes(destination), ref fals_val);
......
......@@ -164,8 +164,7 @@ internal int Format(Span<char> destination)
int count = 0;
char symbol = Symbol;
if (symbol != default &&
(uint)destination.Length == FormatStringLength) // to eliminate bounds checks
if (symbol != default && destination.Length == FormatStringLength)
{
destination[0] = symbol;
count = 1;
......
......@@ -65,7 +65,7 @@ public static bool TryFormat(bool value, Span<byte> destination, out int bytesWr
{
// This check can't be performed earlier because we need to throw if an invalid symbol is
// provided, even if the buffer is too small.
if ((uint)4 >= (uint)destination.Length)
if (destination.Length <= 4)
{
goto BufferTooSmall;
}
......@@ -75,7 +75,7 @@ public static bool TryFormat(bool value, Span<byte> destination, out int bytesWr
}
else if (symbol == 'l')
{
if ((uint)4 >= (uint)destination.Length)
if (destination.Length <= 4)
{
goto BufferTooSmall;
}
......
......@@ -13,9 +13,7 @@ public static partial class Utf8Formatter
//
private static bool TryFormatDateTimeL(DateTime value, Span<byte> destination, out int bytesWritten)
{
// Writing the check in this fashion elides all bounds checks on 'buffer'
// for the remainder of the method.
if ((uint)28 >= (uint)destination.Length)
if (destination.Length <= 28)
{
bytesWritten = 0;
return false;
......
......@@ -13,9 +13,7 @@ public static partial class Utf8Formatter
//
private static bool TryFormatDateTimeR(DateTime value, Span<byte> destination, out int bytesWritten)
{
// Writing the check in this fashion elides all bounds checks on 'buffer'
// for the remainder of the method.
if ((uint)28 >= (uint)destination.Length)
if (destination.Length <= 28)
{
bytesWritten = 0;
return false;
......
......@@ -591,7 +591,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)
{
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index);
}
......@@ -606,7 +606,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);
}
......@@ -626,7 +626,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);
}
......@@ -647,7 +647,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);
}
......@@ -667,8 +667,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);
}
......@@ -710,7 +709,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);
}
......@@ -743,7 +742,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);
}
......@@ -788,7 +787,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);
}
......@@ -813,7 +812,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);
}
......@@ -845,7 +844,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);
}
......@@ -865,7 +864,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);
}
......@@ -885,7 +884,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);
}
......@@ -911,7 +910,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);
}
......@@ -935,7 +934,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);
}
......@@ -957,7 +956,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);
}
......@@ -979,7 +978,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);
}
......@@ -996,7 +995,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);
}
......
......@@ -44,9 +44,12 @@ public int Length
public void Append(T item)
{
int pos = _pos;
if ((uint)pos < (uint)_span.Length)
// Workaround for https://github.com/dotnet/runtime/issues/72004
Span<T> span = _span;
if ((uint)pos < (uint)span.Length)
{
_span[pos] = item;
span[pos] = item;
_pos = pos + 1;
}
else
......
......@@ -602,7 +602,7 @@ public static int[] GetBits(decimal d)
/// <exception cref="ArgumentException">The destination span was not long enough to store the binary representation.</exception>
public static int GetBits(decimal d, Span<int> destination)
{
if ((uint)destination.Length <= 3)
if (destination.Length <= 3)
{
ThrowHelper.ThrowArgumentException_DestinationTooShort();
}
......@@ -623,7 +623,7 @@ public static int GetBits(decimal d, Span<int> destination)
/// <returns>true if the decimal's binary representation was written to the destination; false if the destination wasn't long enough.</returns>
public static bool TryGetBits(decimal d, Span<int> destination, out int valuesWritten)
{
if ((uint)destination.Length <= 3)
if (destination.Length <= 3)
{
valuesWritten = 0;
return false;
......
......@@ -4663,7 +4663,7 @@ private static bool ParseFormatR(ReadOnlySpan<char> source, ref ParsingInfo pars
// Tue, 03 Jan 2017 08:08:05 GMT
// The format is exactly 29 characters.
if ((uint)source.Length != 29)
if (source.Length != 29)
{
result.SetBadDateTimeFailure();
return false;
......@@ -4860,7 +4860,7 @@ private static bool ParseFormatO(ReadOnlySpan<char> source, ref DateTimeResult r
// 2017-06-12T05:30:45.7680000-7:00 (special-case of one-digit offset hour)
// 2017-06-12T05:30:45.7680000-07:00
if ((uint)source.Length < 27 ||
if (source.Length < 27 ||
source[4] != '-' ||
source[7] != '-' ||
source[10] != 'T' ||
......@@ -4981,7 +4981,7 @@ private static bool ParseFormatO(ReadOnlySpan<char> source, ref DateTimeResult r
return false;
}
if ((uint)source.Length > 27)
if (source.Length > 27)
{
char offsetChar = source[27];
switch (offsetChar)
......@@ -4999,7 +4999,7 @@ private static bool ParseFormatO(ReadOnlySpan<char> source, ref DateTimeResult r
case '-':
int offsetHours, colonIndex;
if ((uint)source.Length == 33)
if (source.Length == 33)
{
uint oh1 = (uint)(source[28] - '0'), oh2 = (uint)(source[29] - '0');
......@@ -5012,7 +5012,7 @@ private static bool ParseFormatO(ReadOnlySpan<char> source, ref DateTimeResult r
offsetHours = (int)(oh1 * 10 + oh2);
colonIndex = 30;
}
else if ((uint)source.Length == 32) // special-case allowed for compat: only one offset hour digit
else if (source.Length == 32) // special-case allowed for compat: only one offset hour digit
{
offsetHours = source[28] - '0';
......
......@@ -169,15 +169,17 @@ internal TimeSpanToken GetNextToken()
{
// Get the position of the next character to be processed. If there is no
// next character, we're at the end.
int pos = _pos;
int startPos = _pos;
int pos = startPos;
ReadOnlySpan<char> value = _value;
Debug.Assert(pos > -1);
if (pos >= _value.Length)
if ((uint)pos >= (uint)value.Length)
{
return new TimeSpanToken(TTT.End);
}
// Now retrieve that character. If it's a digit, we're processing a number.
int num = _value[pos] - '0';
int num = value[pos] - '0';
if ((uint)num <= 9)
{
int zeroes = 0;
......@@ -188,8 +190,9 @@ internal TimeSpanToken GetNextToken()
while (true)
{
int digit;
if (++_pos >= _value.Length || (uint)(digit = _value[_pos] - '0') > 9)
if ((uint)++pos >= (uint)value.Length || (uint)(digit = value[pos] - '0') > 9)
{
_pos = pos;
return new TimeSpanToken(TTT.Num, 0, zeroes, default);
}
......@@ -202,12 +205,14 @@ internal TimeSpanToken GetNextToken()
num = digit;
break;
}
_pos = pos;
}
// Continue to read as long as we're reading digits.
while (++_pos < _value.Length)
while ((uint)++pos < (uint)value.Length)
{
int digit = _value[_pos] - '0';
int digit = value[pos] - '0';
if ((uint)digit > 9)
{
break;
......@@ -216,27 +221,26 @@ internal TimeSpanToken GetNextToken()
num = num * 10 + digit;
if ((num & 0xF0000000) != 0) // Max limit we can support 268435455 which is FFFFFFF
{
_pos = pos;
return new TimeSpanToken(TTT.NumOverflow);
}
}
_pos = pos;
return new TimeSpanToken(TTT.Num, num, zeroes, default);
}
// Otherwise, we're processing a separator, and we've already processed the first
// character of it. Continue processing characters as long as they're not digits.
int length = 1;
while (true)
while ((uint)++pos < (uint)value.Length && !char.IsAsciiDigit(value[pos]))
{
if (++_pos >= _value.Length || char.IsAsciiDigit(_value[_pos]))
{
break;
}
length++;
}
_pos = pos;
// Return the separator.
return new TimeSpanToken(TTT.Sep, 0, 0, _value.Slice(pos, length));
return new TimeSpanToken(TTT.Sep, 0, 0, _value.Slice(startPos, length));
}
internal bool EOL => _pos >= (_value.Length - 1);
......@@ -249,7 +253,7 @@ internal void BackOne()
internal char NextChar()
{
int pos = ++_pos;
return (uint)pos < (uint)_value.Length?
return (uint)pos < (uint)_value.Length ?
_value[pos] :
(char)0;
}
......
......@@ -48,7 +48,7 @@ namespace System
// Creates a new guid from a read-only span.
public Guid(ReadOnlySpan<byte> b)
{
if ((uint)b.Length != 16)
if (b.Length != 16)
{
throw new ArgumentException(SR.Format(SR.Arg_GuidArrayCtor, "16"), nameof(b));
}
......@@ -378,7 +378,7 @@ private static bool TryParseExactB(ReadOnlySpan<char> guidString, ref GuidResult
{
// e.g. "{d85b1407-351d-4694-9392-03acc5870eb1}"
if ((uint)guidString.Length != 38 || guidString[0] != '{' || guidString[37] != '}')
if (guidString.Length != 38 || guidString[0] != '{' || guidString[37] != '}')
{
result.SetFailure(overflow: false, nameof(SR.Format_GuidInvLen));
return false;
......@@ -391,7 +391,7 @@ private static bool TryParseExactD(ReadOnlySpan<char> guidString, ref GuidResult
{
// e.g. "d85b1407-351d-4694-9392-03acc5870eb1"
if ((uint)guidString.Length != 36 || guidString[8] != '-' || guidString[13] != '-' || guidString[18] != '-' || guidString[23] != '-')
if (guidString.Length != 36 || guidString[8] != '-' || guidString[13] != '-' || guidString[18] != '-' || guidString[23] != '-')
{
result.SetFailure(overflow: false, guidString.Length != 36 ? nameof(SR.Format_GuidInvLen) : nameof(SR.Format_GuidDashes));
return false;
......@@ -477,7 +477,7 @@ private static bool TryParseExactN(ReadOnlySpan<char> guidString, ref GuidResult
{
// e.g. "d85b1407351d4694939203acc5870eb1"
if ((uint)guidString.Length != 32)
if (guidString.Length != 32)
{
result.SetFailure(overflow: false, nameof(SR.Format_GuidInvLen));
return false;
......@@ -520,7 +520,7 @@ private static bool TryParseExactP(ReadOnlySpan<char> guidString, ref GuidResult
{
// e.g. "(d85b1407-351d-4694-9392-03acc5870eb1)"
if ((uint)guidString.Length != 38 || guidString[0] != '(' || guidString[37] != ')')
if (guidString.Length != 38 || guidString[0] != '(' || guidString[37] != ')')
{
result.SetFailure(overflow: false, nameof(SR.Format_GuidInvLen));
return false;
......@@ -547,7 +547,7 @@ private static bool TryParseExactX(ReadOnlySpan<char> guidString, ref GuidResult
guidString = EatAllWhitespace(guidString);
// Check for leading '{'
if ((uint)guidString.Length == 0 || guidString[0] != '{')
if (guidString.Length == 0 || guidString[0] != '{')
{
result.SetFailure(overflow: false, nameof(SR.Format_GuidBrace));
return false;
......@@ -735,14 +735,14 @@ private static bool TryParseHex(ReadOnlySpan<char> guidString, out uint result)
private static bool TryParseHex(ReadOnlySpan<char> guidString, out uint result, ref bool overflow)
{
if ((uint)guidString.Length > 0)
if (guidString.Length > 0)
{
if (guidString[0] == '+')
{
guidString = guidString.Slice(1);
}
if ((uint)guidString.Length > 1 && guidString[0] == '0' && (guidString[1] | 0x20) == 'x')
if (guidString.Length > 1 && guidString[0] == '0' && (guidString[1] | 0x20) == 'x')
{
guidString = guidString.Slice(2);
}
......
......@@ -154,8 +154,8 @@ public void AppendLiteral(string value)
if (value.Length == 1)
{
Span<char> chars = _chars;
int pos = _pos;
Span<char> chars = _chars;
if ((uint)pos < (uint)chars.Length)
{
chars[pos] = value[0];
......@@ -170,12 +170,11 @@ public void AppendLiteral(string value)
if (value.Length == 2)
{
Span<char> chars = _chars;
int pos = _pos;
if ((uint)pos < chars.Length - 1)
if ((uint)pos < _chars.Length - 1)
{
Unsafe.WriteUnaligned(
ref Unsafe.As<char, byte>(ref Unsafe.Add(ref MemoryMarshal.GetReference(chars), pos)),
ref Unsafe.As<char, byte>(ref Unsafe.Add(ref MemoryMarshal.GetReference(_chars), pos)),
Unsafe.ReadUnaligned<int>(ref Unsafe.As<char, byte>(ref value.GetRawStringData())));
_pos = pos + 2;
}
......
......@@ -842,7 +842,7 @@ private static int ReadRuneFromString(string input, int index)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input);
}
if ((uint)index >= (uint)input!.Length)
if ((uint)index >= (uint)input.Length)
{
ThrowHelper.ThrowArgumentOutOfRange_IndexMustBeLessException();
}
......@@ -1019,7 +1019,7 @@ private static bool TryEncodeToUtf16(Rune value, Span<char> destination, out int
charsWritten = 1;
return true;
}
else if (1 < (uint)destination.Length)
else if (destination.Length > 1)
{
UnicodeUtility.GetUtf16SurrogatesFromSupplementaryPlaneScalar((uint)value._value, out destination[0], out destination[1]);
charsWritten = 2;
......@@ -1066,7 +1066,7 @@ private static bool TryEncodeToUtf8(Rune value, Span<byte> destination, out int
return true;
}
if (1 < (uint)destination.Length)
if (destination.Length > 1)
{
if (value.Value <= 0x7FFu)
{
......@@ -1077,7 +1077,7 @@ private static bool TryEncodeToUtf8(Rune value, Span<byte> destination, out int
return true;
}
if (2 < (uint)destination.Length)
if (destination.Length > 2)
{
if (value.Value <= 0xFFFFu)
{
......@@ -1089,7 +1089,7 @@ private static bool TryEncodeToUtf8(Rune value, Span<byte> destination, out int
return true;
}
if (3 < (uint)destination.Length)
if (destination.Length > 3)
{
// Scalar 000uuuuu zzzzyyyy yyxxxxxx -> bytes [ 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx ]
destination[0] = (byte)((value._value + (0b11110 << 21)) >> 18);
......
......@@ -1052,10 +1052,10 @@ public StringBuilder Append(char value)
int nextCharIndex = m_ChunkLength;
char[] chars = m_ChunkChars;
if ((uint)chars.Length > (uint)nextCharIndex)
if ((uint)nextCharIndex < (uint)chars.Length)
{
chars[nextCharIndex] = value;
m_ChunkLength++;
m_ChunkLength = nextCharIndex + 1;
}
else
{
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册