Browse Source

[libc] Add ffs(), ffsl(), and ffsll()

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 9 years ago
parent
commit
79697c75ee

+ 45
- 1
src/arch/i386/include/bits/strings.h View File

@@ -3,6 +3,50 @@
3 3
 
4 4
 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
5 5
 
6
+/**
7
+ * Find first (i.e. least significant) set bit
8
+ *
9
+ * @v value		Value
10
+ * @ret lsb		Least significant bit set in value (LSB=1), or zero
11
+ */
12
+static inline __attribute__ (( always_inline )) int __ffsl ( long value ) {
13
+	long lsb_minus_one;
14
+
15
+	/* If the input value is zero, the BSF instruction returns
16
+	 * ZF=0 and leaves an undefined value in the output register.
17
+	 * Perform this check in C rather than asm so that it can be
18
+	 * omitted in cases where the compiler is able to prove that
19
+	 * the input is non-zero.
20
+	 */
21
+	if ( value ) {
22
+		__asm__ ( "bsfl %1, %0"
23
+			  : "=r" ( lsb_minus_one )
24
+			  : "rm" ( value ) );
25
+		return ( lsb_minus_one + 1 );
26
+	} else {
27
+		return 0;
28
+	}
29
+}
30
+
31
+/**
32
+ * Find first (i.e. least significant) set bit
33
+ *
34
+ * @v value		Value
35
+ * @ret lsb		Least significant bit set in value (LSB=1), or zero
36
+ */
37
+static inline __attribute__ (( always_inline )) int __ffsll ( long long value ){
38
+	unsigned long high = ( value >> 32 );
39
+	unsigned long low = ( value >> 0 );
40
+
41
+	if ( low ) {
42
+		return ( __ffsl ( low ) );
43
+	} else if ( high ) {
44
+		return ( 32 + __ffsl ( high ) );
45
+	} else {
46
+		return 0;
47
+	}
48
+}
49
+
6 50
 /**
7 51
  * Find last (i.e. most significant) set bit
8 52
  *
@@ -13,7 +57,7 @@ static inline __attribute__ (( always_inline )) int __flsl ( long value ) {
13 57
 	long msb_minus_one;
14 58
 
15 59
 	/* If the input value is zero, the BSR instruction returns
16
-	 * ZF=1 and leaves an undefined value in the output register.
60
+	 * ZF=0 and leaves an undefined value in the output register.
17 61
 	 * Perform this check in C rather than asm so that it can be
18 62
 	 * omitted in cases where the compiler is able to prove that
19 63
 	 * the input is non-zero.

+ 37
- 1
src/arch/x86_64/include/bits/strings.h View File

@@ -3,6 +3,42 @@
3 3
 
4 4
 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
5 5
 
6
+/**
7
+ * Find first (i.e. least significant) set bit
8
+ *
9
+ * @v value		Value
10
+ * @ret lsb		Least significant bit set in value (LSB=1), or zero
11
+ */
12
+static inline __attribute__ (( always_inline )) int __ffsll ( long long value ){
13
+	long long lsb_minus_one;
14
+
15
+	/* If the input value is zero, the BSF instruction returns
16
+	 * ZF=0 and leaves an undefined value in the output register.
17
+	 * Perform this check in C rather than asm so that it can be
18
+	 * omitted in cases where the compiler is able to prove that
19
+	 * the input is non-zero.
20
+	 */
21
+	if ( value ) {
22
+		__asm__ ( "bsfq %1, %0"
23
+			  : "=r" ( lsb_minus_one )
24
+			  : "rm" ( value ) );
25
+		return ( lsb_minus_one + 1 );
26
+	} else {
27
+		return 0;
28
+	}
29
+}
30
+
31
+/**
32
+ * Find first (i.e. least significant) set bit
33
+ *
34
+ * @v value		Value
35
+ * @ret lsb		Least significant bit set in value (LSB=1), or zero
36
+ */
37
+static inline __attribute__ (( always_inline )) int __ffsl ( long value ) {
38
+
39
+	return __ffsll ( value );
40
+}
41
+
6 42
 /**
7 43
  * Find last (i.e. most significant) set bit
8 44
  *
@@ -13,7 +49,7 @@ static inline __attribute__ (( always_inline )) int __flsll ( long long value ){
13 49
 	long long msb_minus_one;
14 50
 
15 51
 	/* If the input value is zero, the BSR instruction returns
16
-	 * ZF=1 and leaves an undefined value in the output register.
52
+	 * ZF=0 and leaves an undefined value in the output register.
17 53
 	 * Perform this check in C rather than asm so that it can be
18 54
 	 * omitted in cases where the compiler is able to prove that
19 55
 	 * the input is non-zero.

+ 77
- 4
src/include/strings.h View File

@@ -12,6 +12,54 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
12 12
 #include <string.h>
13 13
 #include <bits/strings.h>
14 14
 
15
+/**
16
+ * Find first (i.e. least significant) set bit
17
+ *
18
+ * @v x			Value
19
+ * @ret lsb		Least significant bit set in value (LSB=1), or zero
20
+ */
21
+static inline __attribute__ (( always_inline )) int
22
+__constant_ffsll ( unsigned long long x ) {
23
+	int r = 0;
24
+
25
+	if ( ! ( x & 0x00000000ffffffffULL ) ) {
26
+		x >>= 32;
27
+		r += 32;
28
+	}
29
+	if ( ! ( x & 0x0000ffffUL ) ) {
30
+		x >>= 16;
31
+		r += 16;
32
+	}
33
+	if ( ! ( x & 0x00ff ) ) {
34
+		x >>= 8;
35
+		r += 8;
36
+	}
37
+	if ( ! ( x & 0x0f ) ) {
38
+		x >>= 4;
39
+		r += 4;
40
+	}
41
+	if ( ! ( x & 0x3 ) ) {
42
+		x >>= 2;
43
+		r += 2;
44
+	}
45
+	if ( ! ( x & 0x1 ) ) {
46
+		x >>= 1;
47
+		r += 1;
48
+	}
49
+	return ( x ? ( r + 1 ) : 0 );
50
+}
51
+
52
+/**
53
+ * Find first (i.e. least significant) set bit
54
+ *
55
+ * @v x			Value
56
+ * @ret lsb		Least significant bit set in value (LSB=1), or zero
57
+ */
58
+static inline __attribute__ (( always_inline )) int
59
+__constant_ffsl ( unsigned long x ) {
60
+	return __constant_ffsll ( x );
61
+}
62
+
15 63
 /**
16 64
  * Find last (i.e. most significant) set bit
17 65
  *
@@ -46,10 +94,7 @@ __constant_flsll ( unsigned long long x ) {
46 94
 		x >>= 1;
47 95
 		r += 1;
48 96
 	}
49
-	if ( x & 0x1 ) {
50
-		r += 1;
51
-	}
52
-	return r;
97
+	return ( x ? ( r + 1 ) : 0 );
53 98
 }
54 99
 
55 100
 /**
@@ -63,9 +108,37 @@ __constant_flsl ( unsigned long x ) {
63 108
 	return __constant_flsll ( x );
64 109
 }
65 110
 
111
+int __ffsll ( long long x );
112
+int __ffsl ( long x );
66 113
 int __flsll ( long long x );
67 114
 int __flsl ( long x );
68 115
 
116
+/**
117
+ * Find first (i.e. least significant) set bit
118
+ *
119
+ * @v x			Value
120
+ * @ret lsb		Least significant bit set in value (LSB=1), or zero
121
+ */
122
+#define ffsll( x ) \
123
+	( __builtin_constant_p ( x ) ? __constant_ffsll ( x ) : __ffsll ( x ) )
124
+
125
+/**
126
+ * Find first (i.e. least significant) set bit
127
+ *
128
+ * @v x			Value
129
+ * @ret lsb		Least significant bit set in value (LSB=1), or zero
130
+ */
131
+#define ffsl( x ) \
132
+	( __builtin_constant_p ( x ) ? __constant_ffsl ( x ) : __ffsl ( x ) )
133
+
134
+/**
135
+ * Find first (i.e. least significant) set bit
136
+ *
137
+ * @v x			Value
138
+ * @ret lsb		Least significant bit set in value (LSB=1), or zero
139
+ */
140
+#define ffs( x ) ffsl ( x )
141
+
69 142
 /**
70 143
  * Find last (i.e. most significant) set bit
71 144
  *

+ 74
- 0
src/tests/math_test.c View File

@@ -38,6 +38,26 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
38 38
 #include <ipxe/test.h>
39 39
 #include <ipxe/isqrt.h>
40 40
 
41
+/**
42
+ * Force a call to the non-constant implementation of ffsl()
43
+ *
44
+ * @v value		Value
45
+ * @ret lsb		Least significant bit set in value (LSB=1), or zero
46
+ */
47
+__attribute__ (( noinline )) int ffsl_var ( long value ) {
48
+	return ffsl ( value );
49
+}
50
+
51
+/**
52
+ * Force a call to the non-constant implementation of ffsll()
53
+ *
54
+ * @v value		Value
55
+ * @ret lsb		Least significant bit set in value (LSB=1), or zero
56
+ */
57
+__attribute__ (( noinline )) int ffsll_var ( long long value ) {
58
+	return ffsll ( value );
59
+}
60
+
41 61
 /**
42 62
  * Force a call to the non-constant implementation of flsl()
43 63
  *
@@ -176,6 +196,44 @@ __attribute__ (( noinline )) int64_t s64mod_var ( int64_t dividend,
176 196
 	return check_divmod ( dividend, divisor, % );
177 197
 }
178 198
 
199
+/**
200
+ * Report a ffsl() test result
201
+ *
202
+ * @v value		Value
203
+ * @v lsb		Expected LSB
204
+ * @v file		Test code file
205
+ * @v line		Test code line
206
+ */
207
+static inline __attribute__ (( always_inline )) void
208
+ffsl_okx ( long value, int lsb, const char *file, unsigned int line ) {
209
+
210
+	/* Verify as a constant (requires to be inlined) */
211
+	okx ( ffsl ( value ) == lsb, file, line );
212
+
213
+	/* Verify as a non-constant */
214
+	okx ( ffsl_var ( value ) == lsb, file, line );
215
+}
216
+#define ffsl_ok( value, lsb ) ffsl_okx ( value, lsb, __FILE__, __LINE__ )
217
+
218
+/**
219
+ * Report a ffsll() test result
220
+ *
221
+ * @v value		Value
222
+ * @v lsb		Expected LSB
223
+ * @v file		Test code file
224
+ * @v line		Test code line
225
+ */
226
+static inline __attribute__ (( always_inline )) void
227
+ffsll_okx ( long long value, int lsb, const char *file, unsigned int line ) {
228
+
229
+	/* Verify as a constant (requires to be inlined) */
230
+	okx ( ffsll ( value ) == lsb, file, line );
231
+
232
+	/* Verify as a non-constant */
233
+	okx ( ffsll_var ( value ) == lsb, file, line );
234
+}
235
+#define ffsll_ok( value, lsb ) ffsll_okx ( value, lsb, __FILE__, __LINE__ )
236
+
179 237
 /**
180 238
  * Report a flsl() test result
181 239
  *
@@ -274,6 +332,22 @@ static void s64divmod_okx ( int64_t dividend, int64_t divisor,
274 332
  */
275 333
 static void math_test_exec ( void ) {
276 334
 
335
+	/* Test ffsl() */
336
+	ffsl_ok ( 0, 0 );
337
+	ffsl_ok ( 1, 1 );
338
+	ffsl_ok ( 255, 1 );
339
+	ffsl_ok ( 256, 9 );
340
+	ffsl_ok ( 257, 1 );
341
+	ffsl_ok ( 0x54850596, 2 );
342
+	ffsl_ok ( 0x80000000, 32 );
343
+
344
+	/* Test ffsll() */
345
+	ffsll_ok ( 0, 0 );
346
+	ffsll_ok ( 1, 1 );
347
+	ffsll_ok ( 0x6d63623330ULL, 5 );
348
+	ffsll_ok ( 0x80000000UL, 32 );
349
+	ffsll_ok ( 0x8000000000000000ULL, 64 );
350
+
277 351
 	/* Test flsl() */
278 352
 	flsl_ok ( 0, 0 );
279 353
 	flsl_ok ( 1, 1 );

Loading…
Cancel
Save