BitOps.cs 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT license.
  3. // See the LICENSE file in the project root for more information.
  4. using System.Runtime.CompilerServices;
  5. using System.Runtime.InteropServices;
  6. using System.Runtime.Intrinsics.X86;
  7. using Internal.Runtime.CompilerServices;
  8. // Some routines inspired by the Stanford Bit Twiddling Hacks by Sean Eron Anderson:
  9. // http://graphics.stanford.edu/~seander/bithacks.html
  10. namespace System
  11. {
  12. internal static class BitOps
  13. {
  14. // C# no-alloc optimization that directly wraps the data section of the dll (similar to string constants)
  15. // https://github.com/dotnet/roslyn/pull/24621
  16. private static ReadOnlySpan<byte> s_TrailingZeroCountDeBruijn => new byte[32]
  17. {
  18. 00, 01, 28, 02, 29, 14, 24, 03,
  19. 30, 22, 20, 15, 25, 17, 04, 08,
  20. 31, 27, 13, 23, 21, 19, 16, 07,
  21. 26, 12, 18, 06, 11, 05, 10, 09
  22. };
  23. /// <summary>
  24. /// Count the number of trailing zero bits in an integer value.
  25. /// Similar in behavior to the x86 instruction TZCNT.
  26. /// </summary>
  27. /// <param name="value">The value.</param>
  28. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  29. public static int TrailingZeroCount(int value)
  30. => TrailingZeroCount((uint)value);
  31. /// <summary>
  32. /// Count the number of trailing zero bits in an integer value.
  33. /// Similar in behavior to the x86 instruction TZCNT.
  34. /// </summary>
  35. /// <param name="value">The value.</param>
  36. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  37. public static int TrailingZeroCount(uint value)
  38. {
  39. if (Bmi1.IsSupported)
  40. {
  41. // Note that TZCNT contract specifies 0->32
  42. return (int)Bmi1.TrailingZeroCount(value);
  43. }
  44. // Main code has behavior 0->0, so special-case in order to match intrinsic path 0->32
  45. if (value == 0u)
  46. return 32;
  47. // uint.MaxValue >> 27 is always in range [0 - 31] so we use Unsafe.AddByteOffset to avoid bounds check
  48. return Unsafe.AddByteOffset(
  49. ref MemoryMarshal.GetReference(s_TrailingZeroCountDeBruijn),
  50. // Using deBruijn sequence, k=2, n=5 (2^5=32) : 0b_0000_0111_0111_1100_1011_0101_0011_0001u
  51. ((uint)((value & -value) * 0x077CB531u)) >> 27);
  52. }
  53. }
  54. }