From 5f339292561eae17b541525603454173fc03397b Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sat, 5 Nov 2022 16:44:28 +0100 Subject: [PATCH 01/12] Vectorize String.Equals for OrdinalIgnoreCase --- .../src/System/Globalization/Ordinal.cs | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/Ordinal.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/Ordinal.cs index babe60e1af4a9d..2228947de55116 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/Ordinal.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/Ordinal.cs @@ -5,6 +5,7 @@ using System.Text.Unicode; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; namespace System.Globalization { @@ -75,7 +76,78 @@ internal static int CompareStringIgnoreCaseNonAscii(ref char strA, int lengthA, return OrdinalCasing.CompareStringIgnoreCase(ref strA, lengthA, ref strB, lengthB); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool Vector128EqualsIgnoreCaseAscii(Vector128 vec1, Vector128 vec2) + { + Debug.Assert(Vector128AllAscii(vec1)); + Debug.Assert(Vector128AllAscii(vec2)); + + // Works for both 1- and 2-bytes ASCII (to potentially re-use for UTF8) + Vector128 tmp1 = Vector128.Create((sbyte)0x3f) + vec1; + Vector128 tmp2 = Vector128.Create((sbyte)0x3f) + vec2; + tmp1 = Vector128.LessThan(Vector128.Create((byte)0x99).AsSByte(), tmp1); + tmp2 = Vector128.LessThan(Vector128.Create((byte)0x99).AsSByte(), tmp2); + tmp1 = Vector128.AndNot(Vector128.Create((sbyte)0x20), tmp1); + tmp2 = Vector128.AndNot(Vector128.Create((sbyte)0x20), tmp2); + return ((tmp1 + vec1) ^ (tmp2 + vec2)) == Vector128.Zero; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool Vector128AllAscii(Vector128 vec1) + { + return Sse2.And(vec1, Vector128.Create(unchecked((ushort)~0x007F))) == Vector128.Zero; + } + + private static bool EqualsIgnoreCase_Vector128(ref char charA, ref char charB, nuint length) + { + Debug.Assert(length >= (nuint)Vector128.Count); + Debug.Assert(Vector128.IsHardwareAccelerated); + + nuint lengthToExamine = length - (nuint)Vector128.Count; + nuint i = 0; + Vector128 vec1; + Vector128 vec2; + do + { + vec1 = Vector128.LoadUnsafe(ref Unsafe.As(ref charA), i); + vec2 = Vector128.LoadUnsafe(ref Unsafe.As(ref charB), i); + if (!Vector128AllAscii(vec1 | vec2)) + { + goto NON_ASCII; + } + if (!Vector128EqualsIgnoreCaseAscii(vec1.AsSByte(), vec2.AsSByte())) + { + goto NOT_EQUAL; + } + i += (nuint)Vector128.Count; + } while (i <= lengthToExamine); + + // Use scalar path for trailing elements + return i == length || EqualsIgnoreCase(ref Unsafe.Add(ref charA, i), ref Unsafe.Add(ref charB, i), (int)(length - i)); + + NON_ASCII: + if (Vector128AllAscii(vec1) != Vector128AllAscii(vec2)) + { + goto NOT_EQUAL; + } + + return CompareStringIgnoreCase( + ref Unsafe.Add(ref charA, i), (int)(length - i), + ref Unsafe.Add(ref charB, i), (int)(length - i)) == 0; + + NOT_EQUAL: + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static bool EqualsIgnoreCase(ref char charA, ref char charB, int length) + { + if (!Vector128.IsHardwareAccelerated || length <= Vector128.Count) + return EqualsIgnoreCase_Scalar(ref charA, ref charB, length); + return EqualsIgnoreCase_Vector128(ref charA, ref charB, (nuint)length); + } + + internal static bool EqualsIgnoreCase_Scalar(ref char charA, ref char charB, int length) { IntPtr byteOffset = IntPtr.Zero; From 2c6057953204c7fc589c19ac6850e5aa58967ddf Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sat, 5 Nov 2022 16:55:41 +0100 Subject: [PATCH 02/12] Clean up --- .../src/System/Globalization/Ordinal.cs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/Ordinal.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/Ordinal.cs index 2228947de55116..2e8011c594fabe 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/Ordinal.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/Ordinal.cs @@ -95,15 +95,16 @@ private static bool Vector128EqualsIgnoreCaseAscii(Vector128 vec1, Vector [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool Vector128AllAscii(Vector128 vec1) { - return Sse2.And(vec1, Vector128.Create(unchecked((ushort)~0x007F))) == Vector128.Zero; + return (vec1 & Vector128.Create(unchecked((ushort)~0x007F))) == Vector128.Zero; } - private static bool EqualsIgnoreCase_Vector128(ref char charA, ref char charB, nuint length) + private static bool EqualsIgnoreCase_Vector128(ref char charA, ref char charB, int length) { - Debug.Assert(length >= (nuint)Vector128.Count); + Debug.Assert(length >= Vector128.Count); Debug.Assert(Vector128.IsHardwareAccelerated); - nuint lengthToExamine = length - (nuint)Vector128.Count; + nuint lengthU = (nuint)length; + nuint lengthToExamine = lengthU - (nuint)Vector128.Count; nuint i = 0; Vector128 vec1; Vector128 vec2; @@ -123,7 +124,7 @@ private static bool EqualsIgnoreCase_Vector128(ref char charA, ref char charB, n } while (i <= lengthToExamine); // Use scalar path for trailing elements - return i == length || EqualsIgnoreCase(ref Unsafe.Add(ref charA, i), ref Unsafe.Add(ref charB, i), (int)(length - i)); + return i == lengthU || EqualsIgnoreCase(ref Unsafe.Add(ref charA, i), ref Unsafe.Add(ref charB, i), (int)(lengthU - i)); NON_ASCII: if (Vector128AllAscii(vec1) != Vector128AllAscii(vec2)) @@ -132,8 +133,8 @@ private static bool EqualsIgnoreCase_Vector128(ref char charA, ref char charB, n } return CompareStringIgnoreCase( - ref Unsafe.Add(ref charA, i), (int)(length - i), - ref Unsafe.Add(ref charB, i), (int)(length - i)) == 0; + ref Unsafe.Add(ref charA, i), (int)(lengthU - i), + ref Unsafe.Add(ref charB, i), (int)(lengthU - i)) == 0; NOT_EQUAL: return false; @@ -144,7 +145,7 @@ internal static bool EqualsIgnoreCase(ref char charA, ref char charB, int length { if (!Vector128.IsHardwareAccelerated || length <= Vector128.Count) return EqualsIgnoreCase_Scalar(ref charA, ref charB, length); - return EqualsIgnoreCase_Vector128(ref charA, ref charB, (nuint)length); + return EqualsIgnoreCase_Vector128(ref charA, ref charB, length); } internal static bool EqualsIgnoreCase_Scalar(ref char charA, ref char charB, int length) From a92298728926131a776fc9e8a68b1f01821ceca8 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sat, 5 Nov 2022 16:59:06 +0100 Subject: [PATCH 03/12] Clean up --- .../src/System/Globalization/Ordinal.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/Ordinal.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/Ordinal.cs index 2e8011c594fabe..6c2767deab65db 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/Ordinal.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/Ordinal.cs @@ -79,10 +79,8 @@ internal static int CompareStringIgnoreCaseNonAscii(ref char strA, int lengthA, [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool Vector128EqualsIgnoreCaseAscii(Vector128 vec1, Vector128 vec2) { - Debug.Assert(Vector128AllAscii(vec1)); - Debug.Assert(Vector128AllAscii(vec2)); - - // Works for both 1- and 2-bytes ASCII (to potentially re-use for UTF8) + Debug.Assert(Vector128AllAscii(vec1.AsUInt16())); + Debug.Assert(Vector128AllAscii(vec2.AsUInt16())); Vector128 tmp1 = Vector128.Create((sbyte)0x3f) + vec1; Vector128 tmp2 = Vector128.Create((sbyte)0x3f) + vec2; tmp1 = Vector128.LessThan(Vector128.Create((byte)0x99).AsSByte(), tmp1); From 4be63a0d83152ed19fa151ad95cd49e12a70b9df Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sat, 5 Nov 2022 17:36:12 +0100 Subject: [PATCH 04/12] Remove goto, it has no impact on codegen --- .../src/System/Globalization/Ordinal.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/Ordinal.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/Ordinal.cs index 6c2767deab65db..7ef8a8408c9d41 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/Ordinal.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/Ordinal.cs @@ -116,7 +116,7 @@ private static bool EqualsIgnoreCase_Vector128(ref char charA, ref char charB, i } if (!Vector128EqualsIgnoreCaseAscii(vec1.AsSByte(), vec2.AsSByte())) { - goto NOT_EQUAL; + return false; } i += (nuint)Vector128.Count; } while (i <= lengthToExamine); @@ -127,22 +127,21 @@ private static bool EqualsIgnoreCase_Vector128(ref char charA, ref char charB, i NON_ASCII: if (Vector128AllAscii(vec1) != Vector128AllAscii(vec2)) { - goto NOT_EQUAL; + return false; } return CompareStringIgnoreCase( ref Unsafe.Add(ref charA, i), (int)(lengthU - i), ref Unsafe.Add(ref charB, i), (int)(lengthU - i)) == 0; - - NOT_EQUAL: - return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static bool EqualsIgnoreCase(ref char charA, ref char charB, int length) { if (!Vector128.IsHardwareAccelerated || length <= Vector128.Count) + { return EqualsIgnoreCase_Scalar(ref charA, ref charB, length); + } return EqualsIgnoreCase_Vector128(ref charA, ref charB, length); } From 6ca66fa8ec580400ffdb2a2b282af419da1258db Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sat, 5 Nov 2022 17:38:03 +0100 Subject: [PATCH 05/12] Enable 8 chars case for SIMD --- .../System.Private.CoreLib/src/System/Globalization/Ordinal.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/Ordinal.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/Ordinal.cs index 7ef8a8408c9d41..c39473ed91bb68 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/Ordinal.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/Ordinal.cs @@ -138,7 +138,7 @@ ref Unsafe.Add(ref charA, i), (int)(lengthU - i), [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static bool EqualsIgnoreCase(ref char charA, ref char charB, int length) { - if (!Vector128.IsHardwareAccelerated || length <= Vector128.Count) + if (!Vector128.IsHardwareAccelerated || length < Vector128.Count) { return EqualsIgnoreCase_Scalar(ref charA, ref charB, length); } From 042e18acbfee4590eb35444b5d95d9995d1ed2ae Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sat, 5 Nov 2022 19:03:38 +0100 Subject: [PATCH 06/12] Add comments --- .../src/System/Globalization/Ordinal.cs | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/Ordinal.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/Ordinal.cs index c39473ed91bb68..c52667a287687b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/Ordinal.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/Ordinal.cs @@ -79,12 +79,17 @@ internal static int CompareStringIgnoreCaseNonAscii(ref char strA, int lengthA, [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool Vector128EqualsIgnoreCaseAscii(Vector128 vec1, Vector128 vec2) { + // Input is expected to be all-ASCII Debug.Assert(Vector128AllAscii(vec1.AsUInt16())); Debug.Assert(Vector128AllAscii(vec2.AsUInt16())); - Vector128 tmp1 = Vector128.Create((sbyte)0x3f) + vec1; - Vector128 tmp2 = Vector128.Create((sbyte)0x3f) + vec2; - tmp1 = Vector128.LessThan(Vector128.Create((byte)0x99).AsSByte(), tmp1); - tmp2 = Vector128.LessThan(Vector128.Create((byte)0x99).AsSByte(), tmp2); + + Vector128 tmp1 = Vector128.Create((sbyte)(128 - 'A')) + vec1; + Vector128 tmp2 = Vector128.Create((sbyte)(128 - 'A')) + vec2; + + // Calculate no-modify parts + tmp1 = Vector128.LessThan(Vector128.Create((sbyte)(-128 + 25)), tmp1); + tmp2 = Vector128.LessThan(Vector128.Create((sbyte)(-128 + 25)), tmp2); + tmp1 = Vector128.AndNot(Vector128.Create((sbyte)0x20), tmp1); tmp2 = Vector128.AndNot(Vector128.Create((sbyte)0x20), tmp2); return ((tmp1 + vec1) ^ (tmp2 + vec2)) == Vector128.Zero; @@ -110,14 +115,17 @@ private static bool EqualsIgnoreCase_Vector128(ref char charA, ref char charB, i { vec1 = Vector128.LoadUnsafe(ref Unsafe.As(ref charA), i); vec2 = Vector128.LoadUnsafe(ref Unsafe.As(ref charB), i); + if (!Vector128AllAscii(vec1 | vec2)) { goto NON_ASCII; } + if (!Vector128EqualsIgnoreCaseAscii(vec1.AsSByte(), vec2.AsSByte())) { return false; } + i += (nuint)Vector128.Count; } while (i <= lengthToExamine); @@ -125,11 +133,13 @@ private static bool EqualsIgnoreCase_Vector128(ref char charA, ref char charB, i return i == lengthU || EqualsIgnoreCase(ref Unsafe.Add(ref charA, i), ref Unsafe.Add(ref charB, i), (int)(lengthU - i)); NON_ASCII: - if (Vector128AllAscii(vec1) != Vector128AllAscii(vec2)) + if (Vector128AllAscii(vec1) || Vector128AllAscii(vec2)) { + // No need to use the fallback if one of the inputs is full-ASCII return false; } + // Fallback for Non-ASCII inputs return CompareStringIgnoreCase( ref Unsafe.Add(ref charA, i), (int)(lengthU - i), ref Unsafe.Add(ref charB, i), (int)(lengthU - i)) == 0; From ac5edf61009fc6640d87e86beb5101cd0ceb6467 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sat, 5 Nov 2022 22:05:45 +0100 Subject: [PATCH 07/12] Clean up --- .../src/System/Globalization/Ordinal.cs | 31 ++-------------- .../src/System/Text/Unicode/Utf16Utility.cs | 37 +++++++++++++++++++ .../UnrollEqualsStartsWIth_minopts.csproj | 11 ++++++ 3 files changed, 51 insertions(+), 28 deletions(-) create mode 100644 src/tests/JIT/opt/Vectorization/UnrollEqualsStartsWIth_minopts.csproj diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/Ordinal.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/Ordinal.cs index c52667a287687b..3cb8555f9deecf 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/Ordinal.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/Ordinal.cs @@ -76,31 +76,6 @@ internal static int CompareStringIgnoreCaseNonAscii(ref char strA, int lengthA, return OrdinalCasing.CompareStringIgnoreCase(ref strA, lengthA, ref strB, lengthB); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool Vector128EqualsIgnoreCaseAscii(Vector128 vec1, Vector128 vec2) - { - // Input is expected to be all-ASCII - Debug.Assert(Vector128AllAscii(vec1.AsUInt16())); - Debug.Assert(Vector128AllAscii(vec2.AsUInt16())); - - Vector128 tmp1 = Vector128.Create((sbyte)(128 - 'A')) + vec1; - Vector128 tmp2 = Vector128.Create((sbyte)(128 - 'A')) + vec2; - - // Calculate no-modify parts - tmp1 = Vector128.LessThan(Vector128.Create((sbyte)(-128 + 25)), tmp1); - tmp2 = Vector128.LessThan(Vector128.Create((sbyte)(-128 + 25)), tmp2); - - tmp1 = Vector128.AndNot(Vector128.Create((sbyte)0x20), tmp1); - tmp2 = Vector128.AndNot(Vector128.Create((sbyte)0x20), tmp2); - return ((tmp1 + vec1) ^ (tmp2 + vec2)) == Vector128.Zero; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool Vector128AllAscii(Vector128 vec1) - { - return (vec1 & Vector128.Create(unchecked((ushort)~0x007F))) == Vector128.Zero; - } - private static bool EqualsIgnoreCase_Vector128(ref char charA, ref char charB, int length) { Debug.Assert(length >= Vector128.Count); @@ -116,12 +91,12 @@ private static bool EqualsIgnoreCase_Vector128(ref char charA, ref char charB, i vec1 = Vector128.LoadUnsafe(ref Unsafe.As(ref charA), i); vec2 = Vector128.LoadUnsafe(ref Unsafe.As(ref charB), i); - if (!Vector128AllAscii(vec1 | vec2)) + if (!Utf16Utility.AllCharsInVector128AreAscii(vec1 | vec2)) { goto NON_ASCII; } - if (!Vector128EqualsIgnoreCaseAscii(vec1.AsSByte(), vec2.AsSByte())) + if (!Utf16Utility.Vector128OrdinalIgnoreCaseAscii(vec1, vec2)) { return false; } @@ -133,7 +108,7 @@ private static bool EqualsIgnoreCase_Vector128(ref char charA, ref char charB, i return i == lengthU || EqualsIgnoreCase(ref Unsafe.Add(ref charA, i), ref Unsafe.Add(ref charB, i), (int)(lengthU - i)); NON_ASCII: - if (Vector128AllAscii(vec1) || Vector128AllAscii(vec2)) + if (Utf16Utility.AllCharsInVector128AreAscii(vec1) || Utf16Utility.AllCharsInVector128AreAscii(vec2)) { // No need to use the fallback if one of the inputs is full-ASCII return false; diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf16Utility.cs b/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf16Utility.cs index ab75f3e6789d38..2258d9f16cbde6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf16Utility.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf16Utility.cs @@ -3,6 +3,7 @@ using System.Runtime.CompilerServices; using System.Diagnostics; +using System.Runtime.Intrinsics; namespace System.Text.Unicode { @@ -217,5 +218,41 @@ internal static bool UInt64OrdinalIgnoreCaseAscii(ulong valueA, ulong valueB) indicator |= 0xFF7F_FF7F_FF7F_FF7Ful; return (differentBits & indicator) == 0; } + + /// + /// Returns true iff the Vector128 represents 8 ASCII UTF-16 characters in machine endianness. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static bool AllCharsInVector128AreAscii(Vector128 vec) + { + return (vec & Vector128.Create(unchecked((ushort)~0x007F))) == Vector128.Zero; + } + + /// + /// Given two Vector128 that represent 8 ASCII UTF-16 characters each, returns true iff + /// the two inputs are equal using an ordinal case-insensitive comparison. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static bool Vector128OrdinalIgnoreCaseAscii(Vector128 vec1, Vector128 vec2) + { + // ASSUMPTION: Caller has validated that input values are ASCII. + Debug.Assert(AllCharsInVector128AreAscii(vec1)); + Debug.Assert(AllCharsInVector128AreAscii(vec2)); + + // the 0x80 bit of each word of 'lowerIndicator' will be set iff the word has value >= 'A' + Vector128 lowerIndicator1 = Vector128.Create((sbyte)(0x80 - 'A')) + vec1.AsSByte(); + Vector128 lowerIndicator2 = Vector128.Create((sbyte)(0x80 - 'A')) + vec2.AsSByte(); + + // the 0x80 bit of each word of 'combinedIndicator' will be set iff the word has value >= 'A' and <= 'Z' + Vector128 combinedIndicator1 = Vector128.LessThan(Vector128.Create((sbyte)(-128 + 0x80 - 0x5B)), lowerIndicator1); + Vector128 combinedIndicator2 = Vector128.LessThan(Vector128.Create((sbyte)(-128 + 0x80 - 0x5B)), lowerIndicator2); + + // Convert both vectors to lower case by adding 0x20 bit for all [A-Z][a-z] characters + Vector128 lowerCasedVec1 = Vector128.AndNot(Vector128.Create((sbyte)0x20), combinedIndicator1) + vec1.AsSByte(); + Vector128 lowerCasedVec2 = Vector128.AndNot(Vector128.Create((sbyte)0x20), combinedIndicator2) + vec2.AsSByte(); + + // Compare two lowercased vectors + return (lowerCasedVec1 ^ lowerCasedVec2) == Vector128.Zero; + } } } diff --git a/src/tests/JIT/opt/Vectorization/UnrollEqualsStartsWIth_minopts.csproj b/src/tests/JIT/opt/Vectorization/UnrollEqualsStartsWIth_minopts.csproj new file mode 100644 index 00000000000000..c760444563dda9 --- /dev/null +++ b/src/tests/JIT/opt/Vectorization/UnrollEqualsStartsWIth_minopts.csproj @@ -0,0 +1,11 @@ + + + Exe + True + + + + + + + From 8d4c238a3c569e889f385b02a89235e7f7c7c498 Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Sat, 5 Nov 2022 22:05:52 +0100 Subject: [PATCH 08/12] Update src/libraries/System.Private.CoreLib/src/System/Globalization/Ordinal.cs Co-authored-by: Stephen Toub --- .../System.Private.CoreLib/src/System/Globalization/Ordinal.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/Ordinal.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/Ordinal.cs index c52667a287687b..0c24e65c92c3b2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/Ordinal.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/Ordinal.cs @@ -152,6 +152,7 @@ internal static bool EqualsIgnoreCase(ref char charA, ref char charB, int length { return EqualsIgnoreCase_Scalar(ref charA, ref charB, length); } + return EqualsIgnoreCase_Vector128(ref charA, ref charB, length); } From fd46ce4c5d6cd953618c2b82b7085346d7c15656 Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Sat, 5 Nov 2022 23:11:05 +0100 Subject: [PATCH 09/12] Update Utf16Utility.cs --- .../src/System/Text/Unicode/Utf16Utility.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf16Utility.cs b/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf16Utility.cs index 2258d9f16cbde6..e7966e58ecf19d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf16Utility.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf16Utility.cs @@ -240,19 +240,19 @@ internal static bool Vector128OrdinalIgnoreCaseAscii(Vector128 vec1, Vec Debug.Assert(AllCharsInVector128AreAscii(vec2)); // the 0x80 bit of each word of 'lowerIndicator' will be set iff the word has value >= 'A' - Vector128 lowerIndicator1 = Vector128.Create((sbyte)(0x80 - 'A')) + vec1.AsSByte(); - Vector128 lowerIndicator2 = Vector128.Create((sbyte)(0x80 - 'A')) + vec2.AsSByte(); + Vector128 lowerInd1 = Vector128.Create((sbyte)(0x80 - 'A')) + vec1.AsSByte(); + Vector128 lowerInd2 = Vector128.Create((sbyte)(0x80 - 'A')) + vec2.AsSByte(); // the 0x80 bit of each word of 'combinedIndicator' will be set iff the word has value >= 'A' and <= 'Z' - Vector128 combinedIndicator1 = Vector128.LessThan(Vector128.Create((sbyte)(-128 + 0x80 - 0x5B)), lowerIndicator1); - Vector128 combinedIndicator2 = Vector128.LessThan(Vector128.Create((sbyte)(-128 + 0x80 - 0x5B)), lowerIndicator2); + Vector128 combInd1 = Vector128.LessThan(Vector128.Create((sbyte)(-0x80 + 25)), lowerInd1); + Vector128 combInd2 = Vector128.LessThan(Vector128.Create((sbyte)(-0x80 + 25)), lowerInd2); // Convert both vectors to lower case by adding 0x20 bit for all [A-Z][a-z] characters - Vector128 lowerCasedVec1 = Vector128.AndNot(Vector128.Create((sbyte)0x20), combinedIndicator1) + vec1.AsSByte(); - Vector128 lowerCasedVec2 = Vector128.AndNot(Vector128.Create((sbyte)0x20), combinedIndicator2) + vec2.AsSByte(); + Vector128 lcaseVec1 = Vector128.AndNot(Vector128.Create((sbyte)0x20), combInd1) + vec1.AsSByte(); + Vector128 lcaseVec2 = Vector128.AndNot(Vector128.Create((sbyte)0x20), combInd2) + vec2.AsSByte(); // Compare two lowercased vectors - return (lowerCasedVec1 ^ lowerCasedVec2) == Vector128.Zero; + return (lcaseVec1 ^ lcaseVec2) == Vector128.Zero; } } } From 6a9d467f1f313836b129eccac4efbd9ed68f7899 Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Sat, 5 Nov 2022 23:52:53 +0100 Subject: [PATCH 10/12] Update Utf16Utility.cs --- .../src/System/Text/Unicode/Utf16Utility.cs | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf16Utility.cs b/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf16Utility.cs index e7966e58ecf19d..614b7e25115d2e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf16Utility.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf16Utility.cs @@ -236,23 +236,25 @@ internal static bool AllCharsInVector128AreAscii(Vector128 vec) internal static bool Vector128OrdinalIgnoreCaseAscii(Vector128 vec1, Vector128 vec2) { // ASSUMPTION: Caller has validated that input values are ASCII. - Debug.Assert(AllCharsInVector128AreAscii(vec1)); - Debug.Assert(AllCharsInVector128AreAscii(vec2)); // the 0x80 bit of each word of 'lowerIndicator' will be set iff the word has value >= 'A' - Vector128 lowerInd1 = Vector128.Create((sbyte)(0x80 - 'A')) + vec1.AsSByte(); - Vector128 lowerInd2 = Vector128.Create((sbyte)(0x80 - 'A')) + vec2.AsSByte(); + Vector128 lowIndicator1 = Vector128.Create((sbyte)(0x80 - 'A')) + vec1.AsSByte(); + Vector128 lowIndicator2 = Vector128.Create((sbyte)(0x80 - 'A')) + vec2.AsSByte(); // the 0x80 bit of each word of 'combinedIndicator' will be set iff the word has value >= 'A' and <= 'Z' - Vector128 combInd1 = Vector128.LessThan(Vector128.Create((sbyte)(-0x80 + 25)), lowerInd1); - Vector128 combInd2 = Vector128.LessThan(Vector128.Create((sbyte)(-0x80 + 25)), lowerInd2); + Vector128 combIndicator1 = + Vector128.LessThan(Vector128.Create(unchecked((sbyte)(('Z' - 'A') - 0x80))), lowIndicator1); + Vector128 combIndicator2 = + Vector128.LessThan(Vector128.Create(unchecked((sbyte)(('Z' - 'A') - 0x80))), lowIndicator2); // Convert both vectors to lower case by adding 0x20 bit for all [A-Z][a-z] characters - Vector128 lcaseVec1 = Vector128.AndNot(Vector128.Create((sbyte)0x20), combInd1) + vec1.AsSByte(); - Vector128 lcaseVec2 = Vector128.AndNot(Vector128.Create((sbyte)0x20), combInd2) + vec2.AsSByte(); + Vector128 lcVec1 = + Vector128.AndNot(Vector128.Create((sbyte)0x20), combIndicator1) + vec1.AsSByte(); + Vector128 lcVec2 = + Vector128.AndNot(Vector128.Create((sbyte)0x20), combIndicator2) + vec2.AsSByte(); // Compare two lowercased vectors - return (lcaseVec1 ^ lcaseVec2) == Vector128.Zero; + return (lcVec1 ^ lcVec2) == Vector128.Zero; } } } From e6bc04faa31278485db8aa7435abcc77b72bf2e0 Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Sun, 6 Nov 2022 00:11:26 +0100 Subject: [PATCH 11/12] Update Utf16Utility.cs --- .../src/System/Text/Unicode/Utf16Utility.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf16Utility.cs b/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf16Utility.cs index 614b7e25115d2e..fdee767a6aecc2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf16Utility.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf16Utility.cs @@ -242,15 +242,15 @@ internal static bool Vector128OrdinalIgnoreCaseAscii(Vector128 vec1, Vec Vector128 lowIndicator2 = Vector128.Create((sbyte)(0x80 - 'A')) + vec2.AsSByte(); // the 0x80 bit of each word of 'combinedIndicator' will be set iff the word has value >= 'A' and <= 'Z' - Vector128 combIndicator1 = + Vector128 combIndicator1 = Vector128.LessThan(Vector128.Create(unchecked((sbyte)(('Z' - 'A') - 0x80))), lowIndicator1); - Vector128 combIndicator2 = + Vector128 combIndicator2 = Vector128.LessThan(Vector128.Create(unchecked((sbyte)(('Z' - 'A') - 0x80))), lowIndicator2); // Convert both vectors to lower case by adding 0x20 bit for all [A-Z][a-z] characters - Vector128 lcVec1 = + Vector128 lcVec1 = Vector128.AndNot(Vector128.Create((sbyte)0x20), combIndicator1) + vec1.AsSByte(); - Vector128 lcVec2 = + Vector128 lcVec2 = Vector128.AndNot(Vector128.Create((sbyte)0x20), combIndicator2) + vec2.AsSByte(); // Compare two lowercased vectors From 435682fbd169d9bfde29f84cacca9e13c661f737 Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Mon, 7 Nov 2022 22:52:08 +0100 Subject: [PATCH 12/12] Delete MinOpts test - it's too slow --- .../UnrollEqualsStartsWIth_minopts.csproj | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 src/tests/JIT/opt/Vectorization/UnrollEqualsStartsWIth_minopts.csproj diff --git a/src/tests/JIT/opt/Vectorization/UnrollEqualsStartsWIth_minopts.csproj b/src/tests/JIT/opt/Vectorization/UnrollEqualsStartsWIth_minopts.csproj deleted file mode 100644 index c760444563dda9..00000000000000 --- a/src/tests/JIT/opt/Vectorization/UnrollEqualsStartsWIth_minopts.csproj +++ /dev/null @@ -1,11 +0,0 @@ - - - Exe - True - - - - - - -