diff --git a/src/arch/i386/include/bits/strings.h b/src/arch/i386/include/bits/strings.h new file mode 100644 index 00000000..092bcb59 --- /dev/null +++ b/src/arch/i386/include/bits/strings.h @@ -0,0 +1,50 @@ +#ifndef _BITS_STRINGS_H +#define _BITS_STRINGS_H + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** + * Find last (i.e. most significant) set bit + * + * @v value Value + * @ret msb Most significant bit set in value (LSB=1), or zero + */ +static inline __attribute__ (( always_inline )) int __flsl ( long value ) { + long msb_minus_one; + + /* If the input value is zero, the BSR instruction returns + * ZF=1 and leaves an undefined value in the output register. + * Perform this check in C rather than asm so that it can be + * omitted in cases where the compiler is able to prove that + * the input is non-zero. + */ + if ( value ) { + __asm__ ( "bsrl %1, %0" + : "=r" ( msb_minus_one ) + : "rm" ( value ) ); + return ( msb_minus_one + 1 ); + } else { + return 0; + } +} + +/** + * Find last (i.e. most significant) set bit + * + * @v value Value + * @ret msb Most significant bit set in value (LSB=1), or zero + */ +static inline __attribute__ (( always_inline )) int __flsll ( long long value ){ + unsigned long high = ( value >> 32 ); + unsigned long low = ( value >> 0 ); + + if ( high ) { + return ( 32 + __flsl ( high ) ); + } else if ( low ) { + return ( __flsl ( low ) ); + } else { + return 0; + } +} + +#endif /* _BITS_STRINGS_H */ diff --git a/src/arch/x86/include/bits/strings.h b/src/arch/x86_64/include/bits/strings.h similarity index 70% rename from src/arch/x86/include/bits/strings.h rename to src/arch/x86_64/include/bits/strings.h index 62e0fbfb..6ee99a50 100644 --- a/src/arch/x86/include/bits/strings.h +++ b/src/arch/x86_64/include/bits/strings.h @@ -9,8 +9,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); * @v value Value * @ret msb Most significant bit set in value (LSB=1), or zero */ -static inline __attribute__ (( always_inline )) int __flsl ( long value ) { - long msb_minus_one; +static inline __attribute__ (( always_inline )) int __flsll ( long long value ){ + long long msb_minus_one; /* If the input value is zero, the BSR instruction returns * ZF=1 and leaves an undefined value in the output register. @@ -19,7 +19,7 @@ static inline __attribute__ (( always_inline )) int __flsl ( long value ) { * the input is non-zero. */ if ( value ) { - __asm__ ( "bsr %1, %0" + __asm__ ( "bsrq %1, %0" : "=r" ( msb_minus_one ) : "rm" ( value ) ); return ( msb_minus_one + 1 ); @@ -28,4 +28,15 @@ static inline __attribute__ (( always_inline )) int __flsl ( long value ) { } } +/** + * Find last (i.e. most significant) set bit + * + * @v value Value + * @ret msb Most significant bit set in value (LSB=1), or zero + */ +static inline __attribute__ (( always_inline )) int __flsl ( long value ) { + + return __flsll ( value ); +} + #endif /* _BITS_STRINGS_H */ diff --git a/src/include/strings.h b/src/include/strings.h index 924a084f..6912a1e4 100644 --- a/src/include/strings.h +++ b/src/include/strings.h @@ -8,15 +8,13 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include static inline __attribute__ (( always_inline )) int -__constant_flsl ( unsigned long x ) { +__constant_flsll ( unsigned long long x ) { int r = 0; -#if ULONG_MAX > 0xffffffff - if ( x & 0xffffffff00000000UL ) { + if ( x & 0xffffffff00000000ULL ) { x >>= 32; r += 32; } -#endif if ( x & 0xffff0000UL ) { x >>= 16; r += 16; @@ -43,8 +41,17 @@ __constant_flsl ( unsigned long x ) { return r; } +static inline __attribute__ (( always_inline )) int +__constant_flsl ( unsigned long x ) { + return __constant_flsll ( x ); +} + +int __flsll ( long long x ); int __flsl ( long x ); +#define flsll( x ) \ + ( __builtin_constant_p ( x ) ? __constant_flsll ( x ) : __flsll ( x ) ) + #define flsl( x ) \ ( __builtin_constant_p ( x ) ? __constant_flsl ( x ) : __flsl ( x ) ) diff --git a/src/tests/math_test.c b/src/tests/math_test.c index 96cf050b..e12b7939 100644 --- a/src/tests/math_test.c +++ b/src/tests/math_test.c @@ -44,6 +44,16 @@ __attribute__ (( noinline )) int flsl_var ( long value ) { return flsl ( value ); } +/** + * Force a call to the non-constant implementation of flsll() + * + * @v value Value + * @ret msb Most significant bit set in value (LSB=1), or zero + */ +__attribute__ (( noinline )) int flsll_var ( long long value ) { + return flsll ( value ); +} + /** * Check current stack pointer * @@ -181,6 +191,25 @@ flsl_okx ( long value, int msb, const char *file, unsigned int line ) { } #define flsl_ok( value, msb ) flsl_okx ( value, msb, __FILE__, __LINE__ ) +/** + * Report a flsll() test result + * + * @v value Value + * @v msb Expected MSB + * @v file Test code file + * @v line Test code line + */ +static inline __attribute__ (( always_inline )) void +flsll_okx ( long long value, int msb, const char *file, unsigned int line ) { + + /* Verify as a constant (requires to be inlined) */ + okx ( flsll ( value ) == msb, file, line ); + + /* Verify as a non-constant */ + okx ( flsll_var ( value ) == msb, file, line ); +} +#define flsll_ok( value, msb ) flsll_okx ( value, msb, __FILE__, __LINE__ ) + /** * Report a 64-bit unsigned integer division test result * @@ -251,6 +280,14 @@ static void math_test_exec ( void ) { flsl_ok ( -1U, ( 8 * sizeof ( int ) ) ); flsl_ok ( -1UL, ( 8 * sizeof ( long ) ) ); + /* Test flsll() */ + flsll_ok ( 0, 0 ); + flsll_ok ( 1, 1 ); + flsll_ok ( 0x6d63623330ULL, 39 ); + flsll_ok ( -1U, ( 8 * sizeof ( int ) ) ); + flsll_ok ( -1UL, ( 8 * sizeof ( long ) ) ); + flsll_ok ( -1ULL, ( 8 * sizeof ( long long ) ) ); + /* Test 64-bit arithmetic * * On a 64-bit machine, these tests are fairly meaningless.