using Internal.Runtime.CompilerServices;
+// Some routines inspired by the Stanford Bit Twiddling Hacks by Sean Eron Anderson:
+// http://graphics.stanford.edu/~seander/bithacks.html
+
namespace System
{
internal static class BitOps
{
+ // C# no-alloc optimization that directly wraps the data section of the dll (similar to string constants)
+ // https://github.com/dotnet/roslyn/pull/24621
+
+ private static ReadOnlySpan<byte> s_TrailingZeroCountDeBruijn => new byte[32]
+ {
+ 00, 01, 28, 02, 29, 14, 24, 03,
+ 30, 22, 20, 15, 25, 17, 04, 08,
+ 31, 27, 13, 23, 21, 19, 16, 07,
+ 26, 12, 18, 06, 11, 05, 10, 09
+ };
+
+ /// <summary>
+ /// Count the number of trailing zero bits in an integer value.
+ /// Similar in behavior to the x86 instruction TZCNT.
+ /// </summary>
+ /// <param name="value">The value.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int TrailingZeroCount(int value)
+ => TrailingZeroCount((uint)value);
+
+ /// <summary>
+ /// Count the number of trailing zero bits in an integer value.
+ /// Similar in behavior to the x86 instruction TZCNT.
+ /// </summary>
+ /// <param name="value">The value.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static int TrailingZeroCount(int matches)
+ public static int TrailingZeroCount(uint value)
{
if (Bmi1.IsSupported)
{
- return (int)Bmi1.TrailingZeroCount((uint)matches);
+ // Note that TZCNT contract specifies 0->32
+ return (int)Bmi1.TrailingZeroCount(value);
}
- else // Software fallback
- {
- // https://graphics.stanford.edu/~seander/bithacks.html#ZerosOnRightMultLookup
- // uint.MaxValue >> 27 is always in range [0 - 31] so we use Unsafe.AddByteOffset to avoid bounds check
- return Unsafe.AddByteOffset(
- ref MemoryMarshal.GetReference(TrailingCountMultiplyDeBruijn),
- ((uint)((matches & -matches) * 0x077CB531U)) >> 27);
- }
- }
- private static ReadOnlySpan<byte> TrailingCountMultiplyDeBruijn => new byte[32]
- {
- 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
- 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
- };
+ // Main code has behavior 0->0, so special-case in order to match intrinsic path 0->32
+ if (value == 0u)
+ return 32;
+
+ // uint.MaxValue >> 27 is always in range [0 - 31] so we use Unsafe.AddByteOffset to avoid bounds check
+ return Unsafe.AddByteOffset(
+ ref MemoryMarshal.GetReference(s_TrailingZeroCountDeBruijn),
+ // Using deBruijn sequence, k=2, n=5 (2^5=32) : 0b_0000_0111_0111_1100_1011_0101_0011_0001u
+ ((uint)((value & -value) * 0x077CB531u)) >> 27);
+ }
}
}