Use TZCNT and LZCNT for Locate{First|Last}Found{Byte|Char} (#21073)
authorBen Adams <thundercat@illyriad.co.uk>
Mon, 24 Dec 2018 00:26:58 +0000 (00:26 +0000)
committerJan Kotas <jkotas@microsoft.com>
Mon, 24 Dec 2018 00:26:58 +0000 (16:26 -0800)
src/System.Private.CoreLib/shared/System/SpanHelpers.Byte.cs
src/System.Private.CoreLib/shared/System/SpanHelpers.Char.cs

index 118c82b..4f076ee 100644 (file)
@@ -5,6 +5,7 @@
 using System.Diagnostics;
 using System.Runtime.CompilerServices;
 using System.Numerics;
+using System.Runtime.Intrinsics.X86;
 
 using Internal.Runtime.CompilerServices;
 
@@ -1109,23 +1110,39 @@ namespace System
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         private static int LocateFirstFoundByte(ulong match)
         {
-            // Flag least significant power of two bit
-            var powerOfTwoFlag = match ^ (match - 1);
-            // Shift all powers of two into the high byte and extract
-            return (int)((powerOfTwoFlag * XorPowerOfTwoToHighByte) >> 57);
+            // TODO: Arm variants
+            if (Bmi1.X64.IsSupported)
+            {
+                return (int)(Bmi1.X64.TrailingZeroCount(match) >> 3);
+            }
+            else
+            {
+                // Flag least significant power of two bit
+                var powerOfTwoFlag = match ^ (match - 1);
+                // Shift all powers of two into the high byte and extract
+                return (int)((powerOfTwoFlag * XorPowerOfTwoToHighByte) >> 57);
+            }
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         private static int LocateLastFoundByte(ulong match)
         {
-            // Find the most significant byte that has its highest bit set
-            int index = 7;
-            while ((long)match > 0)
+            // TODO: Arm variants
+            if (Lzcnt.X64.IsSupported)
             {
-                match = match << 8;
-                index--;
+                return 7 - (int)(Lzcnt.X64.LeadingZeroCount(match) >> 3);
+            }
+            else
+            {
+                // Find the most significant byte that has its highest bit set
+                int index = 7;
+                while ((long)match > 0)
+                {
+                    match = match << 8;
+                    index--;
+                }
+                return index;
             }
-            return index;
         }
 
         private const ulong XorPowerOfTwoToHighByte = (0x07ul |
index 4701196..d57944e 100644 (file)
@@ -5,10 +5,9 @@
 using System.Diagnostics;
 using System.Runtime.CompilerServices;
 using System.Numerics;
+using System.Runtime.Intrinsics.X86;
 
-#if !netstandard
 using Internal.Runtime.CompilerServices;
-#endif
 
 #if BIT64
 using nuint = System.UInt64;
@@ -822,12 +821,20 @@ namespace System
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         private static int LocateFirstFoundChar(ulong match)
         {
-            unchecked
+            // TODO: Arm variants
+            if (Bmi1.X64.IsSupported)
+            {
+                return (int)(Bmi1.X64.TrailingZeroCount(match) >> 4);
+            }
+            else
             {
-                // Flag least significant power of two bit
-                var powerOfTwoFlag = match ^ (match - 1);
-                // Shift all powers of two into the high byte and extract
-                return (int)((powerOfTwoFlag * XorPowerOfTwoToHighChar) >> 49);
+                unchecked
+                {
+                    // Flag least significant power of two bit
+                    var powerOfTwoFlag = match ^ (match - 1);
+                    // Shift all powers of two into the high byte and extract
+                    return (int)((powerOfTwoFlag * XorPowerOfTwoToHighChar) >> 49);
+                }
             }
         }
 
@@ -859,14 +866,22 @@ namespace System
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         private static int LocateLastFoundChar(ulong match)
         {
-            // Find the most significant char that has its highest bit set
-            int index = 3;
-            while ((long)match > 0)
+            // TODO: Arm variants
+            if (Lzcnt.X64.IsSupported)
             {
-                match = match << 16;
-                index--;
+                return 3 - (int)(Lzcnt.X64.LeadingZeroCount(match) >> 4);
+            }
+            else
+            {
+                // Find the most significant char that has its highest bit set
+                int index = 3;
+                while ((long)match > 0)
+                {
+                    match = match << 16;
+                    index--;
+                }
+                return index;
             }
-            return index;
         }
     }
 }