Browse Source

[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>
tags/v1.20.1
Michael Brown 10 years ago
parent
commit
9e8c48deea
1 changed files with 74 additions and 4 deletions
  1. 74
    4
      src/tests/math_test.c

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

@@ -43,6 +43,72 @@ __attribute__ (( noinline )) int flsl_var ( long value ) {
43 43
 	return flsl ( value );
44 44
 }
45 45
 
46
+/**
47
+ * Check current stack pointer
48
+ *
49
+ * @ret stack		A value at a fixed offset from the current stack pointer
50
+ *
51
+ * Used by check_divmod()
52
+ */
53
+static __attribute__ (( noinline )) void * stack_check ( void ) {
54
+	int a;
55
+	void *ret;
56
+
57
+	/* Hide the fact that we are returning the address of a local
58
+	 * variable, to prevent a compiler warning.
59
+	 */
60
+	__asm__ ( "\n" : "=g" ( ret ) : "0" ( &a ) );
61
+
62
+	return ret;
63
+}
64
+
65
+/**
66
+ * Check division/modulus operation
67
+ *
68
+ * One aspect of the calling convention for the implicit arithmetic
69
+ * functions (__udivmoddi4() etc) is whether the caller or the callee
70
+ * is expected to pop any stack-based arguments.  This distinction can
71
+ * be masked if the compiler chooses to uses a frame pointer in the
72
+ * caller, since the caller will then reload the stack pointer from
73
+ * the frame pointer and so can mask an error in the value of the
74
+ * stack pointer.
75
+ *
76
+ * We run the division operation in a loop, and check that the stack
77
+ * pointer does not change value on the second iteration.  To prevent
78
+ * the compiler from performing various optimisations which might
79
+ * invalidate our intended test (such as unrolling the loop, or moving
80
+ * the division operation outside the loop), we include some dummy
81
+ * inline assembly code.
82
+ */
83
+#define check_divmod( dividend, divisor, OP ) ( {			\
84
+	uint64_t result;						\
85
+	int count = 2;							\
86
+	void *check = NULL;						\
87
+									\
88
+	/* Prevent compiler from unrolling the loop */			\
89
+	__asm__ ( "\n" : "=g" ( count ) : "0" ( count ) );		\
90
+									\
91
+	do {								\
92
+		/* Check that stack pointer does not change between	\
93
+		 * loop iterations.					\
94
+		 */							\
95
+		if ( check ) {						\
96
+			assert ( check == stack_check() );		\
97
+		} else {						\
98
+			check = stack_check();				\
99
+		}							\
100
+									\
101
+		/* Perform division, preventing the compiler from	\
102
+		 * moving the division out of the loop.			\
103
+		 */							\
104
+		__asm__ ( "\n" : "=g" ( dividend ), "=g" ( divisor )	\
105
+			  : "0" ( dividend ), "1" ( divisor ) );	\
106
+	        result = ( dividend OP divisor );			\
107
+		__asm__ ( "\n" : "=g" ( result ) : "0" ( result ) );	\
108
+									\
109
+	} while ( --count );						\
110
+	result; } )
111
+
46 112
 /**
47 113
  * Force a use of runtime 64-bit unsigned integer division
48 114
  *
@@ -52,7 +118,8 @@ __attribute__ (( noinline )) int flsl_var ( long value ) {
52 118
  */
53 119
 __attribute__ (( noinline )) uint64_t u64div_var ( uint64_t dividend,
54 120
 						   uint64_t divisor ) {
55
-	return ( dividend / divisor );
121
+
122
+	return check_divmod ( dividend, divisor, / );
56 123
 }
57 124
 
58 125
 /**
@@ -64,7 +131,8 @@ __attribute__ (( noinline )) uint64_t u64div_var ( uint64_t dividend,
64 131
  */
65 132
 __attribute__ (( noinline )) uint64_t u64mod_var ( uint64_t dividend,
66 133
 						   uint64_t divisor ) {
67
-	return ( dividend % divisor );
134
+
135
+	return check_divmod ( dividend, divisor, % );
68 136
 }
69 137
 
70 138
 /**
@@ -76,7 +144,8 @@ __attribute__ (( noinline )) uint64_t u64mod_var ( uint64_t dividend,
76 144
  */
77 145
 __attribute__ (( noinline )) int64_t s64div_var ( int64_t dividend,
78 146
 						  int64_t divisor ) {
79
-	return ( dividend / divisor );
147
+
148
+	return check_divmod ( dividend, divisor, / );
80 149
 }
81 150
 
82 151
 /**
@@ -88,7 +157,8 @@ __attribute__ (( noinline )) int64_t s64div_var ( int64_t dividend,
88 157
  */
89 158
 __attribute__ (( noinline )) int64_t s64mod_var ( int64_t dividend,
90 159
 						  int64_t divisor ) {
91
-	return ( dividend % divisor );
160
+
161
+	return check_divmod ( dividend, divisor, % );
92 162
 }
93 163
 
94 164
 /**

Loading…
Cancel
Save