3
3
// adding a zero check at the beginning, but `__clzsi2` has a precondition that `x != 0`.
4
4
// Compilers will insert the check for zero in cases where it is needed.
5
5
6
+ use crate :: int:: { CastInto , Int } ;
7
+
6
8
public_test_dep ! {
7
9
/// Returns the number of leading binary zeros in `x`.
8
10
#[ allow( dead_code) ]
9
- pub ( crate ) fn usize_leading_zeros_default ( x: usize ) -> usize {
11
+ pub ( crate ) fn leading_zeros_default< T : Int + CastInto < usize >> ( x: T ) -> usize {
10
12
// The basic idea is to test if the higher bits of `x` are zero and bisect the number
11
13
// of leading zeros. It is possible for all branches of the bisection to use the same
12
14
// code path by conditionally shifting the higher parts down to let the next bisection
@@ -16,46 +18,47 @@ pub(crate) fn usize_leading_zeros_default(x: usize) -> usize {
16
18
// because it simplifies the final bisection step.
17
19
let mut x = x;
18
20
// the number of potential leading zeros
19
- let mut z = usize :: MAX . count_ones ( ) as usize ;
21
+ let mut z = T :: BITS as usize ;
20
22
// a temporary
21
- let mut t: usize ;
22
- #[ cfg( target_pointer_width = "64" ) ]
23
- {
23
+ let mut t: T ;
24
+
25
+ assert!( T :: BITS <= 64 ) ;
26
+ if T :: BITS >= 64 {
24
27
t = x >> 32 ;
25
- if t != 0 {
28
+ if t != T :: ZERO {
26
29
z -= 32 ;
27
30
x = t;
28
31
}
29
32
}
30
- #[ cfg( any( target_pointer_width = "32" , target_pointer_width = "64" ) ) ]
31
- {
33
+ if T :: BITS >= 32 {
32
34
t = x >> 16 ;
33
- if t != 0 {
35
+ if t != T :: ZERO {
34
36
z -= 16 ;
35
37
x = t;
36
38
}
37
39
}
40
+ assert!( T :: BITS >= 16 ) ;
38
41
t = x >> 8 ;
39
- if t != 0 {
42
+ if t != T :: ZERO {
40
43
z -= 8 ;
41
44
x = t;
42
45
}
43
46
t = x >> 4 ;
44
- if t != 0 {
47
+ if t != T :: ZERO {
45
48
z -= 4 ;
46
49
x = t;
47
50
}
48
51
t = x >> 2 ;
49
- if t != 0 {
52
+ if t != T :: ZERO {
50
53
z -= 2 ;
51
54
x = t;
52
55
}
53
56
// the last two bisections are combined into one conditional
54
57
t = x >> 1 ;
55
- if t != 0 {
58
+ if t != T :: ZERO {
56
59
z - 2
57
60
} else {
58
- z - x
61
+ z - x. cast ( )
59
62
}
60
63
61
64
// We could potentially save a few cycles by using the LUT trick from
@@ -80,12 +83,12 @@ pub(crate) fn usize_leading_zeros_default(x: usize) -> usize {
80
83
public_test_dep ! {
81
84
/// Returns the number of leading binary zeros in `x`.
82
85
#[ allow( dead_code) ]
83
- pub ( crate ) fn usize_leading_zeros_riscv ( x: usize ) -> usize {
86
+ pub ( crate ) fn leading_zeros_riscv< T : Int + CastInto < usize >> ( x: T ) -> usize {
84
87
let mut x = x;
85
88
// the number of potential leading zeros
86
- let mut z = usize :: MAX . count_ones ( ) as usize ;
89
+ let mut z = T :: BITS ;
87
90
// a temporary
88
- let mut t: usize ;
91
+ let mut t: u32 ;
89
92
90
93
// RISC-V does not have a set-if-greater-than-or-equal instruction and
91
94
// `(x >= power-of-two) as usize` will get compiled into two instructions, but this is
@@ -95,39 +98,39 @@ pub(crate) fn usize_leading_zeros_riscv(x: usize) -> usize {
95
98
// right). If we try to save an instruction by using `x < imm` for each bisection, we
96
99
// have to shift `x` left and compare with powers of two approaching `usize::MAX + 1`,
97
100
// but the immediate will never fit into 12 bits and never save an instruction.
98
- # [ cfg ( target_pointer_width = "64" ) ]
99
- {
101
+ assert! ( T :: BITS <= 64 ) ;
102
+ if T :: BITS >= 64 {
100
103
// If the upper 32 bits of `x` are not all 0, `t` is set to `1 << 5`, otherwise
101
104
// `t` is set to 0.
102
- t = ( ( x >= ( 1 << 32 ) ) as usize ) << 5 ;
105
+ t = ( ( x >= ( T :: ONE << 32 ) ) as u32 ) << 5 ;
103
106
// If `t` was set to `1 << 5`, then the upper 32 bits are shifted down for the
104
107
// next step to process.
105
108
x >>= t;
106
109
// If `t` was set to `1 << 5`, then we subtract 32 from the number of potential
107
110
// leading zeros
108
111
z -= t;
109
112
}
110
- #[ cfg( any( target_pointer_width = "32" , target_pointer_width = "64" ) ) ]
111
- {
112
- t = ( ( x >= ( 1 << 16 ) ) as usize ) << 4 ;
113
+ if T :: BITS >= 32 {
114
+ t = ( ( x >= ( T :: ONE << 16 ) ) as u32 ) << 4 ;
113
115
x >>= t;
114
116
z -= t;
115
117
}
116
- t = ( ( x >= ( 1 << 8 ) ) as usize ) << 3 ;
118
+ assert!( T :: BITS >= 16 ) ;
119
+ t = ( ( x >= ( T :: ONE << 8 ) ) as u32 ) << 3 ;
117
120
x >>= t;
118
121
z -= t;
119
- t = ( ( x >= ( 1 << 4 ) ) as usize ) << 2 ;
122
+ t = ( ( x >= ( T :: ONE << 4 ) ) as u32 ) << 2 ;
120
123
x >>= t;
121
124
z -= t;
122
- t = ( ( x >= ( 1 << 2 ) ) as usize ) << 1 ;
125
+ t = ( ( x >= ( T :: ONE << 2 ) ) as u32 ) << 1 ;
123
126
x >>= t;
124
127
z -= t;
125
- t = ( x >= ( 1 << 1 ) ) as usize ;
128
+ t = ( x >= ( T :: ONE << 1 ) ) as u32 ;
126
129
x >>= t;
127
130
z -= t;
128
131
// All bits except the LSB are guaranteed to be zero for this final bisection step.
129
132
// If `x != 0` then `x == 1` and subtracts one potential zero from `z`.
130
- z - x
133
+ z as usize - x. cast ( )
131
134
}
132
135
}
133
136
@@ -136,14 +139,40 @@ intrinsics! {
136
139
#[ cfg( any(
137
140
target_pointer_width = "16" ,
138
141
target_pointer_width = "32" ,
139
- target_pointer_width = "64"
140
142
) ) ]
141
- /// Returns the number of leading binary zeros in `x`.
142
- pub extern "C" fn __clzsi2( x: usize ) -> usize {
143
+ /// Returns the number of leading binary zeros in `x`
144
+ pub extern "C" fn __clzsi2( x: u32 ) -> usize {
143
145
if cfg!( any( target_arch = "riscv32" , target_arch = "riscv64" ) ) {
144
- usize_leading_zeros_riscv( x)
146
+ leading_zeros_riscv( x)
147
+ } else {
148
+ leading_zeros_default( x)
149
+ }
150
+ }
151
+
152
+ #[ maybe_use_optimized_c_shim]
153
+ #[ cfg( any(
154
+ target_pointer_width = "32" ,
155
+ target_pointer_width = "64" ,
156
+ ) ) ]
157
+ /// Returns the number of leading binary zeros in `x`
158
+ pub extern "C" fn __clzdi2( x: u64 ) -> usize {
159
+ if cfg!( any( target_arch = "riscv32" , target_arch = "riscv64" ) ) {
160
+ leading_zeros_riscv( x)
161
+ } else {
162
+ leading_zeros_default( x)
163
+ }
164
+ }
165
+
166
+ #[ cfg( any(
167
+ target_pointer_width = "64"
168
+ ) ) ]
169
+ /// Returns the number of leading binary zeros in `x`
170
+ pub extern "C" fn __clzti2( x: u128 ) -> usize {
171
+ let hi = ( x >> 64 ) as u64 ;
172
+ if hi == 0 {
173
+ 64 + __clzdi2( x as u64 )
145
174
} else {
146
- usize_leading_zeros_default ( x )
175
+ __clzdi2 ( hi )
147
176
}
148
177
}
149
178
}
0 commit comments