xref: /minix/minix/tests/test53.c (revision 08cbf5a0)
1 #include <assert.h>
2 #include <minix/u64.h>
3 #include <setjmp.h>
4 #include <signal.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <sys/time.h>
8 #include <unistd.h>
9 
10 #define ERR err(__LINE__)
11 int max_error = 4;
12 #include "common.h"
13 
14 #define TIMED 0
15 
16 
17 static volatile int expect_SIGFPE;
18 static u64_t i, j, k;
19 static jmp_buf jmpbuf_SIGFPE, jmpbuf_main;
20 
21 static void err(int line)
22 {
23 	/* print error information */
24 	printf("error line %d; i=0x%.8lx%.8lx; j=0x%.8lx%.8lx; k=0x%.8lx%.8lx\n",
25 		line,
26 		ex64hi(i), ex64lo(i),
27 		ex64hi(j), ex64lo(j),
28 		ex64hi(k), ex64lo(k));
29 
30 	/* quit after too many errors */
31 	e(7);
32 }
33 
34 #define LENGTHOF(arr) (sizeof(arr) / sizeof(arr[0]))
35 
36 static u64_t getargval(int index, int *done)
37 {
38 	u32_t values[] = {
39 		/* corner cases */
40 		0,
41 		1,
42 		0x7fffffff,
43 		0x80000000,
44 		0x80000001,
45 		0xffffffff,
46 		/* random values */
47 		0xa9,
48 		0x0d88,
49 		0x242811,
50 		0xeb44d1bc,
51 		0x5b,
52 		0xfb50,
53 		0x569c02,
54 		0xb23c8f7d,
55 		0xc3,
56 		0x2366,
57 		0xfabb73,
58 		0xcb4e8aef,
59 		0xe9,
60 		0xffdc,
61 		0x05842d,
62 		0x3fff902d};
63 
64 	assert(done);
65 
66 	/* values with corner case and random 32-bit components */
67 	if (index < LENGTHOF(values) * LENGTHOF(values))
68 		return make64(values[index / LENGTHOF(values)], values[index % LENGTHOF(values)]);
69 
70 	index -= LENGTHOF(values) * LENGTHOF(values);
71 
72 	/* small numbers */
73 	if (index < 16) return make64(index + 2, 0);
74 	index -= 16;
75 
76 	/* big numbers */
77 	if (index < 16) return make64(-index - 2, -1);
78 	index -= 16;
79 
80 	/* powers of two */
81 	if (index < 14) return make64(1 << (index * 2 + 5), 0);
82 	index -= 14;
83 	if (index < 16) return make64(0, 1 << (index * 2 + 1));
84 	index -= 16;
85 
86 	/* done */
87 	*done = 1;
88 	return make64(0, 0);
89 }
90 
91 static void handler_SIGFPE(int signum)
92 {
93 	assert(signum == SIGFPE);
94 
95 	/* restore the signal handler */
96 	if (signal(SIGFPE, handler_SIGFPE) == SIG_ERR) ERR;
97 
98 	/* division by zero occurred, was this expected? */
99 	if (expect_SIGFPE) {
100 		/* expected: jump back to test */
101 		expect_SIGFPE = 0;
102 		longjmp(jmpbuf_SIGFPE, -1);
103 	} else {
104 		/* not expected: error and jump back to main */
105 		longjmp(jmpbuf_main, -1);
106 	}
107 
108 	/* not reachable */
109 	assert(0);
110 	exit(-1);
111 }
112 
113 static inline int bsr64(u64_t i)
114 {
115 	int index;
116 	u64_t mask;
117 
118 	for (index = 63, mask = 1ULL << 63; index >= 0; --index, mask >>= 1) {
119 	    if (i & mask)
120 		return index;
121 	}
122 
123 	return -1;
124 }
125 
126 static void testmul(void)
127 {
128 	int kdone, kidx;
129 	u32_t ilo = ex64lo(i), jlo = ex64lo(j);
130 	u64_t prod = i * j;
131 	int prodbits;
132 
133 	/* compute maximum index of highest-order bit */
134 	prodbits = bsr64(i) + bsr64(j) + 1;
135 	if (i == 0 || j == 0) prodbits = -1;
136 	if (bsr64(prod) > prodbits) ERR;
137 
138 	/* compare to 32-bit multiplication if possible */
139 	if (ex64hi(i) == 0 && ex64hi(j) == 0) {
140 		if (prod != (u64_t)ilo * jlo) ERR;
141 
142 		/* if there is no overflow we can check against pure 32-bit */
143 		if (prodbits < 32 && prod != ilo * jlo) ERR;
144 	}
145 
146 	/* in 32-bit arith low-order DWORD matches regardless of overflow */
147 	if (ex64lo(prod) != ilo * jlo) ERR;
148 
149 	/* multiplication by zero yields zero */
150 	if (prodbits < 0 && prod != 0) ERR;
151 
152 	/* if there is no overflow, check absence of zero divisors */
153 	if (prodbits >= 0 && prodbits < 64 && prod == 0) ERR;
154 
155 	/* commutativity */
156 	if (prod != j * i) ERR;
157 
158 	/* loop though all argument value combinations for third argument */
159 	for (kdone = 0, kidx = 0; k = getargval(kidx, &kdone), !kdone; kidx++) {
160 		/* associativity */
161 		if ((i * j) * k != i * (j * k)) ERR;
162 
163 		/* left and right distributivity */
164 		if ((i + j) * k != (i * k) + (j * k)) ERR;
165 		if (i * (j + k) != (i * j) + (i * k)) ERR;
166 	}
167 }
168 
169 static void do_not_optimize_away(volatile u64_t * ptr)
170 {
171 
172 	/* TODO: does this actually do the job? */
173 	*ptr ^= 1;
174 }
175 
176 static void testdiv0(void)
177 {
178 	int funcidx;
179 	u64_t res;
180 
181 	assert(j == 0);
182 
183 	/* loop through the 5 different division functions */
184 	for (funcidx = 0; funcidx < 5; funcidx++) {
185 		expect_SIGFPE = 1;
186 		if (setjmp(jmpbuf_SIGFPE) == 0) {
187 			/* divide by zero using various functions */
188 			switch (funcidx) {
189 				case 0: res = i / j;		ERR; break;
190 				case 1: res = i / ex64lo(j);	ERR; break;
191 				case 2: res = i / ex64lo(j);	ERR; break;
192 				case 3: res = i % j;		ERR; break;
193 				case 4: res = i % ex64lo(j);	ERR; break;
194 				default: assert(0);		ERR; break;
195 			}
196 
197 			do_not_optimize_away((volatile u64_t *)&res);
198 
199 			/* if we reach this point there was no signal and an
200 			 * error has been recorded
201 			 */
202 			expect_SIGFPE = 0;
203 		} else {
204 			/* a signal has been received and expect_SIGFPE has
205 			 * been reset; all is ok now
206 			 */
207 			assert(!expect_SIGFPE);
208 		}
209 	}
210 }
211 
212 static void testdiv(void)
213 {
214 	u64_t q, r;
215 #if TIMED
216 	struct timeval tvstart, tvend;
217 
218 	printf("i=0x%.8x%.8x; j=0x%.8x%.8x\n",
219 		ex64hi(i), ex64lo(i),
220 		ex64hi(j), ex64lo(j));
221 	fflush(stdout);
222 	if (gettimeofday(&tvstart, NULL) < 0) ERR;
223 #endif
224 
225 	/* division by zero has a separate test */
226 	if (j == 0) {
227 		testdiv0();
228 		return;
229 	}
230 
231 	/* perform division, store q in k to make ERR more informative */
232 	q = i / j;
233 	r = i % j;
234 	k = q;
235 
236 #if TIMED
237 	if (gettimeofday(&tvend, NULL) < 0) ERR;
238 	tvend.tv_sec -= tvstart.tv_sec;
239 	tvend.tv_usec -= tvstart.tv_usec;
240 	if (tvend.tv_usec < 0) {
241 		tvend.tv_sec -= 1;
242 		tvend.tv_usec += 1000000;
243 	}
244 	printf("q=0x%.8x%.8x; r=0x%.8x%.8x; time=%d.%.6d\n",
245 		ex64hi(q), ex64lo(q),
246 		ex64hi(r), ex64lo(r),
247 		tvend.tv_sec, tvend.tv_usec);
248 	fflush(stdout);
249 #endif
250 
251 	/* compare to 64/32-bit division if possible */
252 	if (!ex64hi(j)) {
253 		if (q != i / ex64lo(j)) ERR;
254 		if (!ex64hi(q)) {
255 			if (q != i / ex64lo(j)) ERR;
256 		}
257 		if (r != i % ex64lo(j)) ERR;
258 
259 		/* compare to 32-bit division if possible */
260 		if (!ex64hi(i)) {
261 			if (q != ex64lo(i) / ex64lo(j)) ERR;
262 			if (r != ex64lo(i) % ex64lo(j)) ERR;
263 		}
264 	}
265 
266 	/* check results using i = q j + r and r < j */
267 	if (i != (q * j) + r) ERR;
268 	if (r >= j) ERR;
269 }
270 
271 static void test(void)
272 {
273 	int idone, jdone, iidx, jidx;
274 
275 	/* loop though all argument value combinations */
276 	for (idone = 0, iidx = 0; i = getargval(iidx, &idone), !idone; iidx++)
277 	for (jdone = 0, jidx = 0; j = getargval(jidx, &jdone), !jdone; jidx++) {
278 		testmul();
279 		testdiv();
280 	}
281 }
282 
283 int main(void)
284 {
285 	start(53);
286 
287 	/* set up signal handler to deal with div by zero */
288 	if (setjmp(jmpbuf_main) == 0) {
289 		if (signal(SIGFPE, handler_SIGFPE) == SIG_ERR) ERR;
290 
291 		/* perform tests */
292 		test();
293 	} else {
294 		/* an unexpected SIGFPE has occurred */
295 		ERR;
296 	}
297 
298 	/* this was all */
299 	quit();
300 
301 	return(-1);	/* Unreachable */
302 }
303