1313
1414namespace  System 
1515{ 
16+     /// <summary> 
17+     /// Utility methods for intrinsic bit-twiddling operations. 
18+     /// The methods use hardware intrinsics when available on the underlying platform, 
19+     /// otherwise they use optimized software fallbacks. 
20+     /// </summary> 
1621    internal  static class  BitOps 
1722    { 
1823        // C# no-alloc optimization that directly wraps the data section of the dll (similar to string constants) 
@@ -53,11 +58,11 @@ public static int TrailingZeroCount(uint value)
5358        { 
5459            if  ( Bmi1 . IsSupported ) 
5560            { 
56-                 // Note that  TZCNT contract specifies  0->32 
61+                 // TZCNT contract is  0->32 
5762                return  ( int ) Bmi1 . TrailingZeroCount ( value ) ; 
5863            } 
5964
60-             // Software  fallback has behavior  0->0, so special-case to match intrinsic path 0->32  
65+             // Unguarded  fallback contract is  0->0 
6166            if  ( value  ==  0 ) 
6267            { 
6368                return  32 ; 
@@ -67,8 +72,8 @@ public static int TrailingZeroCount(uint value)
6772            return  Unsafe . AddByteOffset ( 
6873                // Using deBruijn sequence, k=2, n=5 (2^5=32) : 0b_0000_0111_0111_1100_1011_0101_0011_0001u 
6974                ref  MemoryMarshal . GetReference ( s_TrailingZeroCountDeBruijn ) , 
70-                 // long -> IntPtr cast on 32-bit platforms is  expensive - it does  overflow checks not needed here 
71-                 ( IntPtr ) ( int ) ( ( ( uint ) ( ( value  &  - value )  *  0x077CB531u ) )  >>  27 ) ) ;  // shift over long also expensive on 32-bit  
75+                 // uint| long -> IntPtr cast on 32-bit platforms does  expensive overflow checks not needed here 
76+                 ( IntPtr ) ( int ) ( ( ( value  &  ( uint ) - ( int ) value )  *  0x077CB531u )  >>  27 ) ) ;  // Multi-cast mitigates redundant conv.u8  
7277        } 
7378
7479        /// <summary> 
@@ -90,7 +95,7 @@ public static int TrailingZeroCount(ulong value)
9095        { 
9196            if  ( Bmi1 . X64 . IsSupported ) 
9297            { 
93-                 // Note that  TZCNT contract specifies  0->64 
98+                 // TZCNT contract is  0->64 
9499                return  ( int ) Bmi1 . X64 . TrailingZeroCount ( value ) ; 
95100            } 
96101
@@ -114,17 +119,17 @@ public static int LeadingZeroCount(uint value)
114119        { 
115120            if  ( Lzcnt . IsSupported ) 
116121            { 
117-                 // Note that  LZCNT contract specifies  0->32 
122+                 // LZCNT contract is  0->32 
118123                return  ( int ) Lzcnt . LeadingZeroCount ( value ) ; 
119124            } 
120125
121-             // Software  fallback has behavior  0->0, so special-case to match intrinsic path 0->32  
126+             // Unguarded  fallback contract is  0->31  
122127            if  ( value  ==  0 ) 
123128            { 
124129                return  32 ; 
125130            } 
126131
127-             return  31  -  Log2 ( value ) ; 
132+             return  31  -  Log2SoftwareFallback ( value ) ; 
128133        } 
129134
130135        /// <summary> 
@@ -137,7 +142,7 @@ public static int LeadingZeroCount(ulong value)
137142        { 
138143            if  ( Lzcnt . X64 . IsSupported ) 
139144            { 
140-                 // Note that  LZCNT contract specifies  0->64 
145+                 // LZCNT contract is  0->64 
141146                return  ( int ) Lzcnt . X64 . LeadingZeroCount ( value ) ; 
142147            } 
143148
@@ -168,30 +173,32 @@ public static int Log2(uint value)
168173            // 1000..    0      31-0    31 
169174            if  ( Lzcnt . IsSupported ) 
170175            { 
171-                 // Enforce conventional contract 0->0 (since  Log(0) is undefined) 
176+                 // Enforce conventional contract 0->0 (Log(0) is undefined) 
172177                if  ( value  ==  0 ) 
173178                { 
174179                    return  0 ; 
175180                } 
176181
177-                 // Note that  LZCNT contract specifies  0->32 
182+                 // LZCNT contract is  0->32 
178183                return  31  -  ( int ) Lzcnt . LeadingZeroCount ( value ) ; 
179184            } 
180185
181-             // Already has  contract 0->0, without branching  
186+             // Fallback  contract is  0->0 
182187            return  Log2SoftwareFallback ( value ) ; 
183188        } 
184189
185190        /// <summary> 
186191        /// Returns the integer (floor) log of the specified value, base 2. 
187192        /// Note that by convention, input value 0 returns 0 since Log(0) is undefined. 
188-         /// Does not incur branching. 
193+         /// Does not directly use any hardware intrinsics, nor does it  incur branching. 
189194        /// </summary> 
190195        /// <param name="value">The value.</param> 
191196        private  static int  Log2SoftwareFallback ( uint  value ) 
192197        { 
193198            // No AggressiveInlining due to large method size 
199+             // Has conventional contract 0->0 (Log(0) is undefined) 
194200
201+             // Fill trailing zeros with ones, eg 00010010 becomes 00011111 
195202            value  |=  value  >>  01 ; 
196203            value  |=  value  >>  02 ; 
197204            value  |=  value  >>  04 ; 
@@ -202,7 +209,7 @@ private static int Log2SoftwareFallback(uint value)
202209            return  Unsafe . AddByteOffset ( 
203210                // Using deBruijn sequence, k=2, n=5 (2^5=32) : 0b_0000_0111_1100_0100_1010_1100_1101_1101u 
204211                ref  MemoryMarshal . GetReference ( s_Log2DeBruijn ) , 
205-                 // long -> IntPtr cast on 32-bit platforms is  expensive - it does  overflow checks not needed here 
212+                 // uint| long -> IntPtr cast on 32-bit platforms does  expensive overflow checks not needed here 
206213                ( IntPtr ) ( int ) ( ( value  *  0x07C4ACDDu )  >>  27 ) ) ; 
207214        } 
208215
@@ -216,13 +223,13 @@ public static int Log2(ulong value)
216223        { 
217224            if  ( Lzcnt . X64 . IsSupported ) 
218225            { 
219-                 // Enforce conventional contract 0->0 (since  Log(0) is undefined) 
226+                 // Enforce conventional contract 0->0 (Log(0) is undefined) 
220227                if  ( value  ==  0 ) 
221228                { 
222229                    return  0 ; 
223230                } 
224231
225-                 // Note that  LZCNT contract specifies  0->64 
232+                 // LZCNT contract is  0->64 
226233                return  63  -  ( int ) Lzcnt . X64 . LeadingZeroCount ( value ) ; 
227234            } 
228235
@@ -235,5 +242,118 @@ public static int Log2(ulong value)
235242
236243            return  32  +  Log2 ( hi ) ; 
237244        } 
245+ 
246+         /// <summary> 
247+         /// Rotates the specified value left by the specified number of bits. 
248+         /// Similar in behavior to the x86 instruction ROL. 
249+         /// </summary> 
250+         /// <param name="value">The value to rotate.</param> 
251+         /// <param name="offset">The number of bits to rotate by. 
252+         /// Any value outside the range [0..31] is treated as congruent mod 32.</param> 
253+         /// <returns>The rotated value.</returns> 
254+         [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ] 
255+         public  static uint  RotateLeft ( uint  value ,  int  offset ) 
256+             =>  ( value  <<  offset )  |  ( value  >>  ( 32  -  offset ) ) ; 
257+ 
258+         /// <summary> 
259+         /// Rotates the specified value left by the specified number of bits. 
260+         /// Similar in behavior to the x86 instruction ROL. 
261+         /// </summary> 
262+         /// <param name="value">The value to rotate.</param> 
263+         /// <param name="offset">The number of bits to rotate by. 
264+         /// Any value outside the range [0..63] is treated as congruent mod 64.</param> 
265+         /// <returns>The rotated value.</returns> 
266+         [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ] 
267+         public  static ulong  RotateLeft ( ulong  value ,  int  offset ) 
268+             =>  ( value  <<  offset )  |  ( value  >>  ( 64  -  offset ) ) ; 
269+ 
270+         /// <summary> 
271+         /// Rotates the specified value right by the specified number of bits. 
272+         /// Similar in behavior to the x86 instruction ROR. 
273+         /// </summary> 
274+         /// <param name="value">The value to rotate.</param> 
275+         /// <param name="offset">The number of bits to rotate by. 
276+         /// Any value outside the range [0..31] is treated as congruent mod 32.</param> 
277+         /// <returns>The rotated value.</returns> 
278+         [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ] 
279+         public  static uint  RotateRight ( uint  value ,  int  offset ) 
280+             =>  ( value  >>  offset )  |  ( value  <<  ( 32  -  offset ) ) ; 
281+ 
282+         /// <summary> 
283+         /// Rotates the specified value right by the specified number of bits. 
284+         /// Similar in behavior to the x86 instruction ROR. 
285+         /// </summary> 
286+         /// <param name="value">The value to rotate.</param> 
287+         /// <param name="offset">The number of bits to rotate by. 
288+         /// Any value outside the range [0..63] is treated as congruent mod 64.</param> 
289+         /// <returns>The rotated value.</returns> 
290+         [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ] 
291+         public  static ulong  RotateRight ( ulong  value ,  int  offset ) 
292+             =>  ( value  >>  offset )  |  ( value  <<  ( 64  -  offset ) ) ; 
293+ 
294+         /// <summary> 
295+         /// Returns the population count (number of bits set) of a mask. 
296+         /// Similar in behavior to the x86 instruction POPCNT. 
297+         /// </summary> 
298+         /// <param name="value">The value.</param> 
299+         [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ] 
300+         public  static int  PopCount ( uint  value ) 
301+         { 
302+             if  ( Popcnt . IsSupported ) 
303+             { 
304+                 return  ( int ) Popcnt . PopCount ( value ) ; 
305+             } 
306+ 
307+             return  SoftwareFallback ( value ) ; 
308+ 
309+             int  SoftwareFallback ( uint  v ) 
310+             { 
311+                 const  uint  c1  =  0x_55555555u ; 
312+                 const  uint  c2  =  0x_33333333u ; 
313+                 const  uint  c3  =  0x_0F0F0F0Fu ; 
314+                 const  uint  c4  =  0x_01010101u ; 
315+ 
316+                 v  =  v  -  ( ( v  >>  1 )  &  c1 ) ; 
317+                 v  =  ( v  &  c2 )  +  ( ( v  >>  2 )  &  c2 ) ; 
318+                 v  =  ( ( ( v  +  ( v  >>  4 ) )  &  c3 )  *  c4 )  >>  24 ; 
319+ 
320+                 return  ( int ) v ; 
321+             } 
322+         } 
323+ 
324+         /// <summary> 
325+         /// Returns the population count (number of bits set) of a mask. 
326+         /// Similar in behavior to the x86 instruction POPCNT. 
327+         /// </summary> 
328+         /// <param name="value">The value.</param> 
329+         [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ] 
330+         public  static int  PopCount ( ulong  value ) 
331+         { 
332+             if  ( Popcnt . X64 . IsSupported ) 
333+             { 
334+                 return  ( int ) Popcnt . X64 . PopCount ( value ) ; 
335+             } 
336+ 
337+ #if BIT32 
338+             return  PopCount ( ( uint ) value )  // lo 
339+                 +  PopCount ( ( uint ) ( value  >>  32 ) ) ;  // hi 
340+ #else
341+             return  SoftwareFallback ( value ) ; 
342+ 
343+             int  SoftwareFallback ( ulong  v )  
344+             { 
345+                 const  ulong  c1  =  0x_55555555_55555555ul ; 
346+                 const  ulong  c2  =  0x_33333333_33333333ul ; 
347+                 const  ulong  c3  =  0x_0F0F0F0F_0F0F0F0Ful ; 
348+                 const  ulong  c4  =  0x_01010101_01010101ul ; 
349+ 
350+                 v  =  v  -  ( ( v  >>  1 )  &  c1 ) ; 
351+                 v  =  ( v  &  c2 )  +  ( ( v  >>  2 )  &  c2 ) ; 
352+                 v  =  ( ( ( v  +  ( v  >>  4 ) )  &  c3 )  *  c4 )  >>  56 ; 
353+ 
354+                 return  ( int ) v ; 
355+             } 
356+ #endif
357+         } 
238358    } 
239359} 
0 commit comments