| /* |
| * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>. |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License as |
| * published by the Free Software Foundation; either version 2 of the |
| * License, or any later version. |
| * |
| * This program is distributed in the hope that it will be useful, but |
| * WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
| * 02110-1301, USA. |
| * |
| * You can also choose to distribute this program under the terms of |
| * the Unmodified Binary Distribution Licence (as given in the file |
| * COPYING.UBDL), provided that you have satisfied its requirements. |
| */ |
| |
| FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); |
| |
| /** @file |
| * |
| * Mathematical self-tests |
| * |
| */ |
| |
| /* Forcibly enable assertions */ |
| #undef NDEBUG |
| |
| #include <string.h> |
| #include <strings.h> |
| #include <assert.h> |
| #include <ipxe/test.h> |
| #include <ipxe/isqrt.h> |
| |
| /** |
| * Force a call to the non-constant implementation of ffsl() |
| * |
| * @v value Value |
| * @ret lsb Least significant bit set in value (LSB=1), or zero |
| */ |
| __attribute__ (( noinline )) int ffsl_var ( long value ) { |
| return ffsl ( value ); |
| } |
| |
| /** |
| * Force a call to the non-constant implementation of ffsll() |
| * |
| * @v value Value |
| * @ret lsb Least significant bit set in value (LSB=1), or zero |
| */ |
| __attribute__ (( noinline )) int ffsll_var ( long long value ) { |
| return ffsll ( value ); |
| } |
| |
| /** |
| * Force a call to the non-constant implementation of flsl() |
| * |
| * @v value Value |
| * @ret msb Most significant bit set in value (LSB=1), or zero |
| */ |
| __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 ); |
| } |
| |
| /** |
| * Force a use of runtime 64-bit shift left |
| * |
| * @v value Value |
| * @v shift Shift amount |
| * @ret value Shifted value |
| */ |
| __attribute__ (( noinline )) uint64_t lsl64_var ( uint64_t value, |
| unsigned int shift ) { |
| return ( value << shift ); |
| } |
| |
| /** |
| * Force a use of runtime 64-bit logical shift right |
| * |
| * @v value Value |
| * @v shift Shift amount |
| * @ret value Shifted value |
| */ |
| __attribute__ (( noinline )) uint64_t lsr64_var ( uint64_t value, |
| unsigned int shift ) { |
| return ( value >> shift ); |
| } |
| |
| /** |
| * Force a use of runtime 64-bit arithmetic shift right |
| * |
| * @v value Value |
| * @v shift Shift amount |
| * @ret value Shifted value |
| */ |
| __attribute__ (( noinline )) int64_t asr64_var ( int64_t value, |
| unsigned int shift ) { |
| return ( value >> shift ); |
| } |
| |
| /** |
| * Check current stack pointer |
| * |
| * @ret stack A value at a fixed offset from the current stack pointer |
| * |
| * Used by check_divmod() |
| */ |
| static __attribute__ (( noinline )) void * stack_check ( void ) { |
| int a; |
| void *ret; |
| |
| /* Hide the fact that we are returning the address of a local |
| * variable, to prevent a compiler warning. |
| */ |
| __asm__ ( "\n" : "=g" ( ret ) : "0" ( &a ) ); |
| |
| return ret; |
| } |
| |
| /** |
| * Check division/modulus operation |
| * |
| * One aspect of the calling convention for the implicit arithmetic |
| * functions (__udivmoddi4() etc) is whether the caller or the callee |
| * is expected to pop any stack-based arguments. This distinction can |
| * be masked if the compiler chooses to uses a frame pointer in the |
| * caller, since the caller will then reload the stack pointer from |
| * the frame pointer and so can mask an error in the value of the |
| * stack pointer. |
| * |
| * We run the division operation in a loop, and check that the stack |
| * pointer does not change value on the second iteration. To prevent |
| * the compiler from performing various optimisations which might |
| * invalidate our intended test (such as unrolling the loop, or moving |
| * the division operation outside the loop), we include some dummy |
| * inline assembly code. |
| */ |
| #define check_divmod( dividend, divisor, OP ) ( { \ |
| uint64_t result; \ |
| int count = 2; \ |
| void *check = NULL; \ |
| \ |
| /* Prevent compiler from unrolling the loop */ \ |
| __asm__ ( "\n" : "=g" ( count ) : "0" ( count ) ); \ |
| \ |
| do { \ |
| /* Check that stack pointer does not change between \ |
| * loop iterations. \ |
| */ \ |
| if ( check ) { \ |
| assert ( check == stack_check() ); \ |
| } else { \ |
| check = stack_check(); \ |
| } \ |
| \ |
| /* Perform division, preventing the compiler from \ |
| * moving the division out of the loop. \ |
| */ \ |
| __asm__ ( "\n" : "=g" ( dividend ), "=g" ( divisor ) \ |
| : "0" ( dividend ), "1" ( divisor ) ); \ |
| result = ( dividend OP divisor ); \ |
| __asm__ ( "\n" : "=g" ( result ) : "0" ( result ) ); \ |
| \ |
| } while ( --count ); \ |
| result; } ) |
| |
| /** |
| * Force a use of runtime 64-bit unsigned integer division |
| * |
| * @v dividend Dividend |
| * @v divisor Divisor |
| * @ret quotient Quotient |
| */ |
| __attribute__ (( noinline )) uint64_t u64div_var ( uint64_t dividend, |
| uint64_t divisor ) { |
| |
| return check_divmod ( dividend, divisor, / ); |
| } |
| |
| /** |
| * Force a use of runtime 64-bit unsigned integer modulus |
| * |
| * @v dividend Dividend |
| * @v divisor Divisor |
| * @ret remainder Remainder |
| */ |
| __attribute__ (( noinline )) uint64_t u64mod_var ( uint64_t dividend, |
| uint64_t divisor ) { |
| |
| return check_divmod ( dividend, divisor, % ); |
| } |
| |
| /** |
| * Force a use of runtime 64-bit signed integer division |
| * |
| * @v dividend Dividend |
| * @v divisor Divisor |
| * @ret quotient Quotient |
| */ |
| __attribute__ (( noinline )) int64_t s64div_var ( int64_t dividend, |
| int64_t divisor ) { |
| |
| return check_divmod ( dividend, divisor, / ); |
| } |
| |
| /** |
| * Force a use of runtime 64-bit unsigned integer modulus |
| * |
| * @v dividend Dividend |
| * @v divisor Divisor |
| * @ret remainder Remainder |
| */ |
| __attribute__ (( noinline )) int64_t s64mod_var ( int64_t dividend, |
| int64_t divisor ) { |
| |
| return check_divmod ( dividend, divisor, % ); |
| } |
| |
| /** |
| * Report a ffsl() test result |
| * |
| * @v value Value |
| * @v lsb Expected LSB |
| * @v file Test code file |
| * @v line Test code line |
| */ |
| static inline __attribute__ (( always_inline )) void |
| ffsl_okx ( long value, int lsb, const char *file, unsigned int line ) { |
| |
| /* Verify as a constant (requires to be inlined) */ |
| okx ( ffsl ( value ) == lsb, file, line ); |
| |
| /* Verify as a non-constant */ |
| okx ( ffsl_var ( value ) == lsb, file, line ); |
| } |
| #define ffsl_ok( value, lsb ) ffsl_okx ( value, lsb, __FILE__, __LINE__ ) |
| |
| /** |
| * Report a ffsll() test result |
| * |
| * @v value Value |
| * @v lsb Expected LSB |
| * @v file Test code file |
| * @v line Test code line |
| */ |
| static inline __attribute__ (( always_inline )) void |
| ffsll_okx ( long long value, int lsb, const char *file, unsigned int line ) { |
| |
| /* Verify as a constant (requires to be inlined) */ |
| okx ( ffsll ( value ) == lsb, file, line ); |
| |
| /* Verify as a non-constant */ |
| okx ( ffsll_var ( value ) == lsb, file, line ); |
| } |
| #define ffsll_ok( value, lsb ) ffsll_okx ( value, lsb, __FILE__, __LINE__ ) |
| |
| /** |
| * Report a flsl() 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 |
| flsl_okx ( long value, int msb, const char *file, unsigned int line ) { |
| |
| /* Verify as a constant (requires to be inlined) */ |
| okx ( flsl ( value ) == msb, file, line ); |
| |
| /* Verify as a non-constant */ |
| okx ( flsl_var ( value ) == msb, file, 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 shift left test result |
| * |
| * @v value Value |
| * @v shift Shift amount |
| * @v expected Expected value |
| * @v file Test code file |
| * @v line Test code line |
| */ |
| static inline __attribute__ (( always_inline )) void |
| lsl64_okx ( uint64_t value, unsigned int shift, uint64_t expected, |
| const char *file, unsigned int line ) { |
| |
| /* Verify as a compile-time calculation */ |
| okx ( ( value << shift ) == expected, file, line ); |
| |
| /* Verify as a runtime calculation */ |
| okx ( lsl64_var ( value, shift ) == expected, file, line ); |
| } |
| #define lsl64_ok( value, shift, expected ) \ |
| lsl64_okx ( value, shift, expected, __FILE__, __LINE__ ) |
| |
| /** |
| * Report a 64-bit logical shift right test result |
| * |
| * @v value Value |
| * @v shift Shift amount |
| * @v expected Expected value |
| * @v file Test code file |
| * @v line Test code line |
| */ |
| static inline __attribute__ (( always_inline )) void |
| lsr64_okx ( uint64_t value, unsigned int shift, uint64_t expected, |
| const char *file, unsigned int line ) { |
| |
| /* Verify as a compile-time calculation */ |
| okx ( ( value >> shift ) == expected, file, line ); |
| |
| /* Verify as a runtime calculation */ |
| okx ( lsr64_var ( value, shift ) == expected, file, line ); |
| } |
| #define lsr64_ok( value, shift, expected ) \ |
| lsr64_okx ( value, shift, expected, __FILE__, __LINE__ ) |
| |
| /** |
| * Report a 64-bit arithmetic shift right test result |
| * |
| * @v value Value |
| * @v shift Shift amount |
| * @v expected Expected value |
| * @v file Test code file |
| * @v line Test code line |
| */ |
| static inline __attribute__ (( always_inline )) void |
| asr64_okx ( int64_t value, unsigned int shift, int64_t expected, |
| const char *file, unsigned int line ) { |
| |
| /* Verify as a compile-time calculation */ |
| okx ( ( value >> shift ) == expected, file, line ); |
| |
| /* Verify as a runtime calculation */ |
| okx ( asr64_var ( value, shift ) == expected, file, line ); |
| } |
| #define asr64_ok( value, shift, expected ) \ |
| asr64_okx ( value, shift, expected, __FILE__, __LINE__ ) |
| |
| /** |
| * Report a 64-bit unsigned integer division test result |
| * |
| * @v dividend Dividend |
| * @v divisor Divisor |
| * @v quotient Quotient |
| * @v remainder Remainder |
| * @v file Test code file |
| * @v line Test code line |
| */ |
| static void u64divmod_okx ( uint64_t dividend, uint64_t divisor, |
| uint64_t quotient, uint64_t remainder, |
| const char *file, unsigned int line ) { |
| |
| /* Sanity check */ |
| okx ( ( ( divisor * quotient ) + remainder ) == dividend, file, line ); |
| |
| /* Check division */ |
| okx ( u64div_var ( dividend, divisor ) == quotient, file, line ); |
| |
| /* Check modulus */ |
| okx ( u64mod_var ( dividend, divisor ) == remainder, file, line ); |
| } |
| #define u64divmod_ok( dividend, divisor, quotient, remainder ) \ |
| u64divmod_okx ( dividend, divisor, quotient, remainder, \ |
| __FILE__, __LINE__ ) |
| |
| /** |
| * Report a 64-bit signed integer division test result |
| * |
| * @v dividend Dividend |
| * @v divisor Divisor |
| * @v quotient Quotient |
| * @v remainder Remainder |
| * @v file Test code file |
| * @v line Test code line |
| */ |
| static void s64divmod_okx ( int64_t dividend, int64_t divisor, |
| int64_t quotient, int64_t remainder, |
| const char *file, unsigned int line ) { |
| |
| /* Sanity check */ |
| okx ( ( ( divisor * quotient ) + remainder ) == dividend, file, line ); |
| |
| /* Check division */ |
| okx ( s64div_var ( dividend, divisor ) == quotient, file, line ); |
| |
| /* Check modulus */ |
| okx ( s64mod_var ( dividend, divisor ) == remainder, file, line ); |
| } |
| #define s64divmod_ok( dividend, divisor, quotient, remainder ) \ |
| s64divmod_okx ( dividend, divisor, quotient, remainder, \ |
| __FILE__, __LINE__ ) |
| |
| /** |
| * Perform mathematical self-tests |
| * |
| */ |
| static void math_test_exec ( void ) { |
| |
| /* Test ffsl() */ |
| ffsl_ok ( 0, 0 ); |
| ffsl_ok ( 1, 1 ); |
| ffsl_ok ( 255, 1 ); |
| ffsl_ok ( 256, 9 ); |
| ffsl_ok ( 257, 1 ); |
| ffsl_ok ( 0x54850596, 2 ); |
| ffsl_ok ( 0x80000000, 32 ); |
| |
| /* Test ffsll() */ |
| ffsll_ok ( 0, 0 ); |
| ffsll_ok ( 1, 1 ); |
| ffsll_ok ( 0x6d63623330ULL, 5 ); |
| ffsll_ok ( 0x80000000UL, 32 ); |
| ffsll_ok ( 0x8000000000000000ULL, 64 ); |
| |
| /* Test flsl() */ |
| flsl_ok ( 0, 0 ); |
| flsl_ok ( 1, 1 ); |
| flsl_ok ( 255, 8 ); |
| flsl_ok ( 256, 9 ); |
| flsl_ok ( 257, 9 ); |
| flsl_ok ( 0x69505845, 31 ); |
| 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. |
| * |
| * On a 32-bit machine, these tests verify the correct |
| * operation of our libgcc functions __udivmoddi4() |
| * etc. (including checking that the implicit calling |
| * convention assumed by gcc matches our expectations). |
| */ |
| lsl64_ok ( 0x06760c14710540c2ULL, 0, 0x06760c14710540c2ULL ); |
| lsr64_ok ( 0x06760c14710540c2ULL, 0, 0x06760c14710540c2ULL ); |
| asr64_ok ( 0x06760c14710540c2ULL, 0, 0x06760c14710540c2ULL ); |
| lsl64_ok ( 0xccafd1a8cb724c13ULL, 20, 0x1a8cb724c1300000ULL ); |
| lsr64_ok ( 0xccafd1a8cb724c13ULL, 20, 0x00000ccafd1a8cb7ULL ); |
| asr64_ok ( 0xccafd1a8cb724c13ULL, 20, 0xfffffccafd1a8cb7ULL ); |
| lsl64_ok ( 0x83567264b1234518ULL, 32, 0xb123451800000000ULL ); |
| lsr64_ok ( 0x83567264b1234518ULL, 32, 0x0000000083567264ULL ); |
| asr64_ok ( 0x83567264b1234518ULL, 32, 0xffffffff83567264ULL ); |
| lsl64_ok ( 0x69ee42fcbf1a4ea4ULL, 47, 0x2752000000000000ULL ); |
| lsr64_ok ( 0x69ee42fcbf1a4ea4ULL, 47, 0x000000000000d3dcULL ); |
| asr64_ok ( 0x69ee42fcbf1a4ea4ULL, 47, 0x000000000000d3dcULL ); |
| lsl64_ok ( 0xaa20b8caddee4269ULL, 63, 0x8000000000000000ULL ); |
| lsr64_ok ( 0xaa20b8caddee4269ULL, 63, 0x0000000000000001ULL ); |
| asr64_ok ( 0xaa20b8caddee4269ULL, 63, 0xffffffffffffffffULL ); |
| u64divmod_ok ( 0x2b90ddccf699f765ULL, 0xed9f5e73ULL, |
| 0x2eef6ab4ULL, 0x0e12f089ULL ); |
| s64divmod_ok ( 0x2b90ddccf699f765ULL, 0xed9f5e73ULL, |
| 0x2eef6ab4ULL, 0x0e12f089ULL ); |
| u64divmod_ok ( 0xc09e00dcb9e34b54ULL, 0x35968185cdc744f3ULL, |
| 3, 0x1fda7c4b508d7c7bULL ); |
| s64divmod_ok ( -0x3f61ff23461cb4acLL, 0x35968185cdc744f3ULL, |
| -1LL, -0x9cb7d9d78556fb9LL ); |
| u64divmod_ok ( 0, 0x5b2f2737f4ffULL, 0, 0 ); |
| s64divmod_ok ( 0, 0xbb00ded72766207fULL, 0, 0 ); |
| |
| /* Test integer square root */ |
| ok ( isqrt ( 0 ) == 0 ); |
| ok ( isqrt ( 1 ) == 1 ); |
| ok ( isqrt ( 255 ) == 15 ); |
| ok ( isqrt ( 256 ) == 16 ); |
| ok ( isqrt ( 257 ) == 16 ); |
| ok ( isqrt ( 0xa53df2adUL ) == 52652 ); |
| ok ( isqrt ( 0x123793c6UL ) == 17482 ); |
| ok ( isqrt ( -1UL ) == ( -1UL >> ( 8 * sizeof ( unsigned long ) / 2 ))); |
| } |
| |
| /** Mathematical self-tests */ |
| struct self_test math_test __self_test = { |
| .name = "math", |
| .exec = math_test_exec, |
| }; |