Skip to content

Commit c956f69

Browse files
authored
Fix recent IndexOf regressions (#80779)
* Improve IndexOf codegen for non-char types * Call directly into NonPackedIndexOf where it makes sense
1 parent cdf90c6 commit c956f69

File tree

4 files changed

+42
-17
lines changed

4 files changed

+42
-17
lines changed

src/libraries/System.Private.CoreLib/src/System/SpanHelpers.Char.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ public static int IndexOf(ref char searchSpace, int searchSpaceLength, ref char
4040
while (remainingSearchSpaceLength > 0)
4141
{
4242
// Do a quick search for the first element of "value".
43-
int relativeIndex = IndexOfChar(ref Unsafe.Add(ref searchSpace, offset), valueHead, remainingSearchSpaceLength);
43+
// Using the non-packed variant as the input is short and would not benefit from the packed implementation.
44+
int relativeIndex = NonPackedIndexOfChar(ref Unsafe.Add(ref searchSpace, offset), valueHead, remainingSearchSpaceLength);
4445
if (relativeIndex < 0)
4546
break;
4647

src/libraries/System.Private.CoreLib/src/System/SpanHelpers.Packed.cs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,14 @@ internal static partial class PackedSpanHelpers
2424

2525
// Not all values can benefit from packing the searchSpace. See comments in PackSources below.
2626
[MethodImpl(MethodImplOptions.AggressiveInlining)]
27-
public static unsafe bool CanUsePackedIndexOf<T>(T value) =>
28-
PackedIndexOfIsSupported &&
29-
RuntimeHelpers.IsBitwiseEquatable<T>() &&
30-
sizeof(T) == sizeof(ushort) &&
31-
*(ushort*)&value - 1u < 254u;
27+
public static unsafe bool CanUsePackedIndexOf<T>(T value)
28+
{
29+
Debug.Assert(PackedIndexOfIsSupported);
30+
Debug.Assert(RuntimeHelpers.IsBitwiseEquatable<T>());
31+
Debug.Assert(sizeof(T) == sizeof(ushort));
32+
33+
return *(ushort*)&value - 1u < 254u;
34+
}
3235

3336
[MethodImpl(MethodImplOptions.AggressiveInlining)]
3437
public static int IndexOf(ref char searchSpace, char value, int length) =>

src/libraries/System.Private.CoreLib/src/System/SpanHelpers.T.cs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1306,7 +1306,7 @@ public static int SequenceCompareTo<T>(ref T first, int firstLength, ref T secon
13061306
[MethodImpl(MethodImplOptions.AggressiveInlining)]
13071307
internal static unsafe bool ContainsValueType<T>(ref T searchSpace, T value, int length) where T : struct, INumber<T>
13081308
{
1309-
if (PackedSpanHelpers.PackedIndexOfIsSupported && PackedSpanHelpers.CanUsePackedIndexOf(value))
1309+
if (PackedSpanHelpers.PackedIndexOfIsSupported && typeof(T) == typeof(short) && PackedSpanHelpers.CanUsePackedIndexOf(value))
13101310
{
13111311
return PackedSpanHelpers.Contains(ref Unsafe.As<T, short>(ref searchSpace), *(short*)&value, length);
13121312
}
@@ -1435,6 +1435,10 @@ internal static bool NonPackedContainsValueType<T>(ref T searchSpace, T value, i
14351435
internal static int IndexOfChar(ref char searchSpace, char value, int length)
14361436
=> IndexOfValueType(ref Unsafe.As<char, short>(ref searchSpace), (short)value, length);
14371437

1438+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
1439+
internal static int NonPackedIndexOfChar(ref char searchSpace, char value, int length) =>
1440+
NonPackedIndexOfValueType<short, DontNegate<short>>(ref Unsafe.As<char, short>(ref searchSpace), (short)value, length);
1441+
14381442
[MethodImpl(MethodImplOptions.AggressiveInlining)]
14391443
internal static int IndexOfValueType<T>(ref T searchSpace, T value, int length) where T : struct, INumber<T>
14401444
=> IndexOfValueType<T, DontNegate<T>>(ref searchSpace, value, length);
@@ -1448,7 +1452,7 @@ private static unsafe int IndexOfValueType<TValue, TNegator>(ref TValue searchSp
14481452
where TValue : struct, INumber<TValue>
14491453
where TNegator : struct, INegator<TValue>
14501454
{
1451-
if (PackedSpanHelpers.PackedIndexOfIsSupported && PackedSpanHelpers.CanUsePackedIndexOf(value))
1455+
if (PackedSpanHelpers.PackedIndexOfIsSupported && typeof(TValue) == typeof(short) && PackedSpanHelpers.CanUsePackedIndexOf(value))
14521456
{
14531457
return typeof(TNegator) == typeof(DontNegate<short>)
14541458
? PackedSpanHelpers.IndexOf(ref Unsafe.As<TValue, char>(ref searchSpace), *(char*)&value, length)
@@ -1605,7 +1609,7 @@ private static unsafe int IndexOfAnyValueType<TValue, TNegator>(ref TValue searc
16051609
where TValue : struct, INumber<TValue>
16061610
where TNegator : struct, INegator<TValue>
16071611
{
1608-
if (PackedSpanHelpers.PackedIndexOfIsSupported && PackedSpanHelpers.CanUsePackedIndexOf(value0) && PackedSpanHelpers.CanUsePackedIndexOf(value1))
1612+
if (PackedSpanHelpers.PackedIndexOfIsSupported && typeof(TValue) == typeof(short) && PackedSpanHelpers.CanUsePackedIndexOf(value0) && PackedSpanHelpers.CanUsePackedIndexOf(value1))
16091613
{
16101614
return typeof(TNegator) == typeof(DontNegate<short>)
16111615
? PackedSpanHelpers.IndexOfAny(ref Unsafe.As<TValue, char>(ref searchSpace), *(char*)&value0, *(char*)&value1, length)
@@ -1782,7 +1786,7 @@ private static unsafe int IndexOfAnyValueType<TValue, TNegator>(ref TValue searc
17821786
where TValue : struct, INumber<TValue>
17831787
where TNegator : struct, INegator<TValue>
17841788
{
1785-
if (PackedSpanHelpers.PackedIndexOfIsSupported && PackedSpanHelpers.CanUsePackedIndexOf(value0) && PackedSpanHelpers.CanUsePackedIndexOf(value1) && PackedSpanHelpers.CanUsePackedIndexOf(value2))
1789+
if (PackedSpanHelpers.PackedIndexOfIsSupported && typeof(TValue) == typeof(short) && PackedSpanHelpers.CanUsePackedIndexOf(value0) && PackedSpanHelpers.CanUsePackedIndexOf(value1) && PackedSpanHelpers.CanUsePackedIndexOf(value2))
17861790
{
17871791
return typeof(TNegator) == typeof(DontNegate<short>)
17881792
? PackedSpanHelpers.IndexOfAny(ref Unsafe.As<TValue, char>(ref searchSpace), *(char*)&value0, *(char*)&value1, *(char*)&value2, length)
@@ -3085,7 +3089,7 @@ private static unsafe int IndexOfAnyInRangeUnsignedNumber<T, TNegator>(ref T sea
30853089
where T : struct, IUnsignedNumber<T>, IComparisonOperators<T, T, bool>
30863090
where TNegator : struct, INegator<T>
30873091
{
3088-
if (PackedSpanHelpers.PackedIndexOfIsSupported && PackedSpanHelpers.CanUsePackedIndexOf(lowInclusive) && PackedSpanHelpers.CanUsePackedIndexOf(highInclusive) && highInclusive >= lowInclusive)
3092+
if (PackedSpanHelpers.PackedIndexOfIsSupported && typeof(T) == typeof(ushort) && PackedSpanHelpers.CanUsePackedIndexOf(lowInclusive) && PackedSpanHelpers.CanUsePackedIndexOf(highInclusive) && highInclusive >= lowInclusive)
30893093
{
30903094
ref char charSearchSpace = ref Unsafe.As<T, char>(ref searchSpace);
30913095
char charLowInclusive = *(char*)&lowInclusive;

src/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1074,15 +1074,32 @@ public string Replace(string oldValue, string? newValue)
10741074
// Find all occurrences of the oldValue character.
10751075
char c = oldValue[0];
10761076
int i = 0;
1077-
while (true)
1077+
1078+
if (PackedSpanHelpers.PackedIndexOfIsSupported && PackedSpanHelpers.CanUsePackedIndexOf(c))
10781079
{
1079-
int pos = SpanHelpers.IndexOfChar(ref Unsafe.Add(ref _firstChar, i), c, Length - i);
1080-
if (pos < 0)
1080+
while (true)
10811081
{
1082-
break;
1082+
int pos = PackedSpanHelpers.IndexOf(ref Unsafe.Add(ref _firstChar, i), c, Length - i);
1083+
if (pos < 0)
1084+
{
1085+
break;
1086+
}
1087+
replacementIndices.Append(i + pos);
1088+
i += pos + 1;
1089+
}
1090+
}
1091+
else
1092+
{
1093+
while (true)
1094+
{
1095+
int pos = SpanHelpers.NonPackedIndexOfChar(ref Unsafe.Add(ref _firstChar, i), c, Length - i);
1096+
if (pos < 0)
1097+
{
1098+
break;
1099+
}
1100+
replacementIndices.Append(i + pos);
1101+
i += pos + 1;
10831102
}
1084-
replacementIndices.Append(i + pos);
1085-
i += pos + 1;
10861103
}
10871104
}
10881105
else

0 commit comments

Comments
 (0)