diff options
author | Michael Brown | 2014-04-26 17:00:26 +0200 |
---|---|---|
committer | Michael Brown | 2014-04-26 17:00:26 +0200 |
commit | 9e8c48deea90a601fdd45e8d7428d4eceb7e6a39 (patch) | |
tree | 0f564bc7431296e7650a5573cdd5a33792a68cb6 /src/tests | |
parent | [build] Fix __libgcc attribute for recent gcc versions (diff) | |
download | ipxe-9e8c48deea90a601fdd45e8d7428d4eceb7e6a39.tar.gz ipxe-9e8c48deea90a601fdd45e8d7428d4eceb7e6a39.tar.xz ipxe-9e8c48deea90a601fdd45e8d7428d4eceb7e6a39.zip |
[test] Check for correct -mrtd assumption on libgcc arithmetic functions
As observed in commit 082cedb ("[build] Fix __libgcc attribute for
recent gcc versions"), recent versions of gcc have changed the
semantics of -mrtd as applied to the implicit arithmetic functions.
It is possible for tests to succeed even if our assumptions about
gcc's interpretation of -mrtd are incorrect. In particular, if gcc
chooses to utilise a frame pointer in the calling function, then it
can tolerate a temporarily incorrect stack pointer (since the stack
pointer will shortly afterwards be restored from the frame pointer
anyway).
Add tests designed specifically to check that our implementations of
the implicit arithmetic functions manipulate the stack pointer as
expected by gcc.
The effect of these tests can be observed by temporarily reverting
commit 082cedb ("[build] Fix __libgcc attribute for recent gcc
versions"): without this fix in place, the tests will fail on gcc 4.7
and later.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src/tests')
-rw-r--r-- | src/tests/math_test.c | 78 |
1 files changed, 74 insertions, 4 deletions
diff --git a/src/tests/math_test.c b/src/tests/math_test.c index fe61e29c..14c9ee3e 100644 --- a/src/tests/math_test.c +++ b/src/tests/math_test.c @@ -44,6 +44,72 @@ __attribute__ (( noinline )) int flsl_var ( long value ) { } /** + * 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 @@ -52,7 +118,8 @@ __attribute__ (( noinline )) int flsl_var ( long value ) { */ __attribute__ (( noinline )) uint64_t u64div_var ( uint64_t dividend, uint64_t divisor ) { - return ( dividend / divisor ); + + return check_divmod ( dividend, divisor, / ); } /** @@ -64,7 +131,8 @@ __attribute__ (( noinline )) uint64_t u64div_var ( uint64_t dividend, */ __attribute__ (( noinline )) uint64_t u64mod_var ( uint64_t dividend, uint64_t divisor ) { - return ( dividend % divisor ); + + return check_divmod ( dividend, divisor, % ); } /** @@ -76,7 +144,8 @@ __attribute__ (( noinline )) uint64_t u64mod_var ( uint64_t dividend, */ __attribute__ (( noinline )) int64_t s64div_var ( int64_t dividend, int64_t divisor ) { - return ( dividend / divisor ); + + return check_divmod ( dividend, divisor, / ); } /** @@ -88,7 +157,8 @@ __attribute__ (( noinline )) int64_t s64div_var ( int64_t dividend, */ __attribute__ (( noinline )) int64_t s64mod_var ( int64_t dividend, int64_t divisor ) { - return ( dividend % divisor ); + + return check_divmod ( dividend, divisor, % ); } /** |