1433d6423SLionel Sambuc #include <assert.h>
2433d6423SLionel Sambuc #include <minix/u64.h>
3433d6423SLionel Sambuc #include <setjmp.h>
4433d6423SLionel Sambuc #include <signal.h>
5433d6423SLionel Sambuc #include <stdio.h>
6433d6423SLionel Sambuc #include <stdlib.h>
7433d6423SLionel Sambuc #include <sys/time.h>
8433d6423SLionel Sambuc #include <unistd.h>
9433d6423SLionel Sambuc
10433d6423SLionel Sambuc #define ERR err(__LINE__)
11433d6423SLionel Sambuc int max_error = 4;
12433d6423SLionel Sambuc #include "common.h"
13433d6423SLionel Sambuc
14433d6423SLionel Sambuc #define TIMED 0
15433d6423SLionel Sambuc
16433d6423SLionel Sambuc
17433d6423SLionel Sambuc static volatile int expect_SIGFPE;
18433d6423SLionel Sambuc static u64_t i, j, k;
19433d6423SLionel Sambuc static jmp_buf jmpbuf_SIGFPE, jmpbuf_main;
20433d6423SLionel Sambuc
err(int line)21433d6423SLionel Sambuc static void err(int line)
22433d6423SLionel Sambuc {
23433d6423SLionel Sambuc /* print error information */
24433d6423SLionel Sambuc printf("error line %d; i=0x%.8lx%.8lx; j=0x%.8lx%.8lx; k=0x%.8lx%.8lx\n",
25433d6423SLionel Sambuc line,
26433d6423SLionel Sambuc ex64hi(i), ex64lo(i),
27433d6423SLionel Sambuc ex64hi(j), ex64lo(j),
28433d6423SLionel Sambuc ex64hi(k), ex64lo(k));
29433d6423SLionel Sambuc
30433d6423SLionel Sambuc /* quit after too many errors */
31433d6423SLionel Sambuc e(7);
32433d6423SLionel Sambuc }
33433d6423SLionel Sambuc
34433d6423SLionel Sambuc #define LENGTHOF(arr) (sizeof(arr) / sizeof(arr[0]))
35433d6423SLionel Sambuc
getargval(int index,int * done)36433d6423SLionel Sambuc static u64_t getargval(int index, int *done)
37433d6423SLionel Sambuc {
38433d6423SLionel Sambuc u32_t values[] = {
39433d6423SLionel Sambuc /* corner cases */
40433d6423SLionel Sambuc 0,
41433d6423SLionel Sambuc 1,
42433d6423SLionel Sambuc 0x7fffffff,
43433d6423SLionel Sambuc 0x80000000,
44433d6423SLionel Sambuc 0x80000001,
45433d6423SLionel Sambuc 0xffffffff,
46433d6423SLionel Sambuc /* random values */
47433d6423SLionel Sambuc 0xa9,
48433d6423SLionel Sambuc 0x0d88,
49433d6423SLionel Sambuc 0x242811,
50433d6423SLionel Sambuc 0xeb44d1bc,
51433d6423SLionel Sambuc 0x5b,
52433d6423SLionel Sambuc 0xfb50,
53433d6423SLionel Sambuc 0x569c02,
54433d6423SLionel Sambuc 0xb23c8f7d,
55433d6423SLionel Sambuc 0xc3,
56433d6423SLionel Sambuc 0x2366,
57433d6423SLionel Sambuc 0xfabb73,
58433d6423SLionel Sambuc 0xcb4e8aef,
59433d6423SLionel Sambuc 0xe9,
60433d6423SLionel Sambuc 0xffdc,
61433d6423SLionel Sambuc 0x05842d,
62433d6423SLionel Sambuc 0x3fff902d};
63433d6423SLionel Sambuc
64433d6423SLionel Sambuc assert(done);
65433d6423SLionel Sambuc
66433d6423SLionel Sambuc /* values with corner case and random 32-bit components */
67433d6423SLionel Sambuc if (index < LENGTHOF(values) * LENGTHOF(values))
68433d6423SLionel Sambuc return make64(values[index / LENGTHOF(values)], values[index % LENGTHOF(values)]);
69433d6423SLionel Sambuc
70433d6423SLionel Sambuc index -= LENGTHOF(values) * LENGTHOF(values);
71433d6423SLionel Sambuc
72433d6423SLionel Sambuc /* small numbers */
73433d6423SLionel Sambuc if (index < 16) return make64(index + 2, 0);
74433d6423SLionel Sambuc index -= 16;
75433d6423SLionel Sambuc
76433d6423SLionel Sambuc /* big numbers */
77433d6423SLionel Sambuc if (index < 16) return make64(-index - 2, -1);
78433d6423SLionel Sambuc index -= 16;
79433d6423SLionel Sambuc
80433d6423SLionel Sambuc /* powers of two */
81433d6423SLionel Sambuc if (index < 14) return make64(1 << (index * 2 + 5), 0);
82433d6423SLionel Sambuc index -= 14;
83433d6423SLionel Sambuc if (index < 16) return make64(0, 1 << (index * 2 + 1));
84433d6423SLionel Sambuc index -= 16;
85433d6423SLionel Sambuc
86433d6423SLionel Sambuc /* done */
87433d6423SLionel Sambuc *done = 1;
88433d6423SLionel Sambuc return make64(0, 0);
89433d6423SLionel Sambuc }
90433d6423SLionel Sambuc
handler_SIGFPE(int signum)91433d6423SLionel Sambuc static void handler_SIGFPE(int signum)
92433d6423SLionel Sambuc {
93433d6423SLionel Sambuc assert(signum == SIGFPE);
94433d6423SLionel Sambuc
95433d6423SLionel Sambuc /* restore the signal handler */
96433d6423SLionel Sambuc if (signal(SIGFPE, handler_SIGFPE) == SIG_ERR) ERR;
97433d6423SLionel Sambuc
98433d6423SLionel Sambuc /* division by zero occurred, was this expected? */
99433d6423SLionel Sambuc if (expect_SIGFPE) {
100433d6423SLionel Sambuc /* expected: jump back to test */
101433d6423SLionel Sambuc expect_SIGFPE = 0;
102433d6423SLionel Sambuc longjmp(jmpbuf_SIGFPE, -1);
103433d6423SLionel Sambuc } else {
104433d6423SLionel Sambuc /* not expected: error and jump back to main */
105433d6423SLionel Sambuc longjmp(jmpbuf_main, -1);
106433d6423SLionel Sambuc }
107433d6423SLionel Sambuc
108433d6423SLionel Sambuc /* not reachable */
109433d6423SLionel Sambuc assert(0);
110433d6423SLionel Sambuc exit(-1);
111433d6423SLionel Sambuc }
112433d6423SLionel Sambuc
bsr64(u64_t i)113433d6423SLionel Sambuc static inline int bsr64(u64_t i)
114433d6423SLionel Sambuc {
115433d6423SLionel Sambuc int index;
116433d6423SLionel Sambuc u64_t mask;
117433d6423SLionel Sambuc
118433d6423SLionel Sambuc for (index = 63, mask = 1ULL << 63; index >= 0; --index, mask >>= 1) {
119433d6423SLionel Sambuc if (i & mask)
120433d6423SLionel Sambuc return index;
121433d6423SLionel Sambuc }
122433d6423SLionel Sambuc
123433d6423SLionel Sambuc return -1;
124433d6423SLionel Sambuc }
125433d6423SLionel Sambuc
testmul(void)126433d6423SLionel Sambuc static void testmul(void)
127433d6423SLionel Sambuc {
128433d6423SLionel Sambuc int kdone, kidx;
129433d6423SLionel Sambuc u32_t ilo = ex64lo(i), jlo = ex64lo(j);
130433d6423SLionel Sambuc u64_t prod = i * j;
131433d6423SLionel Sambuc int prodbits;
132433d6423SLionel Sambuc
133433d6423SLionel Sambuc /* compute maximum index of highest-order bit */
134433d6423SLionel Sambuc prodbits = bsr64(i) + bsr64(j) + 1;
135433d6423SLionel Sambuc if (i == 0 || j == 0) prodbits = -1;
136433d6423SLionel Sambuc if (bsr64(prod) > prodbits) ERR;
137433d6423SLionel Sambuc
138433d6423SLionel Sambuc /* compare to 32-bit multiplication if possible */
139433d6423SLionel Sambuc if (ex64hi(i) == 0 && ex64hi(j) == 0) {
140433d6423SLionel Sambuc if (prod != (u64_t)ilo * jlo) ERR;
141433d6423SLionel Sambuc
142433d6423SLionel Sambuc /* if there is no overflow we can check against pure 32-bit */
143433d6423SLionel Sambuc if (prodbits < 32 && prod != ilo * jlo) ERR;
144433d6423SLionel Sambuc }
145433d6423SLionel Sambuc
146433d6423SLionel Sambuc /* in 32-bit arith low-order DWORD matches regardless of overflow */
147433d6423SLionel Sambuc if (ex64lo(prod) != ilo * jlo) ERR;
148433d6423SLionel Sambuc
149433d6423SLionel Sambuc /* multiplication by zero yields zero */
150433d6423SLionel Sambuc if (prodbits < 0 && prod != 0) ERR;
151433d6423SLionel Sambuc
152433d6423SLionel Sambuc /* if there is no overflow, check absence of zero divisors */
153433d6423SLionel Sambuc if (prodbits >= 0 && prodbits < 64 && prod == 0) ERR;
154433d6423SLionel Sambuc
155433d6423SLionel Sambuc /* commutativity */
156433d6423SLionel Sambuc if (prod != j * i) ERR;
157433d6423SLionel Sambuc
158433d6423SLionel Sambuc /* loop though all argument value combinations for third argument */
159433d6423SLionel Sambuc for (kdone = 0, kidx = 0; k = getargval(kidx, &kdone), !kdone; kidx++) {
160433d6423SLionel Sambuc /* associativity */
161433d6423SLionel Sambuc if ((i * j) * k != i * (j * k)) ERR;
162433d6423SLionel Sambuc
163433d6423SLionel Sambuc /* left and right distributivity */
164433d6423SLionel Sambuc if ((i + j) * k != (i * k) + (j * k)) ERR;
165433d6423SLionel Sambuc if (i * (j + k) != (i * j) + (i * k)) ERR;
166433d6423SLionel Sambuc }
167433d6423SLionel Sambuc }
168433d6423SLionel Sambuc
do_not_optimize_away(volatile u64_t * ptr)169*3083d603SDavid van Moolenbroek static void do_not_optimize_away(volatile u64_t * ptr)
170*3083d603SDavid van Moolenbroek {
171*3083d603SDavid van Moolenbroek
172*3083d603SDavid van Moolenbroek /* TODO: does this actually do the job? */
173*3083d603SDavid van Moolenbroek *ptr ^= 1;
174*3083d603SDavid van Moolenbroek }
175*3083d603SDavid van Moolenbroek
testdiv0(void)176433d6423SLionel Sambuc static void testdiv0(void)
177433d6423SLionel Sambuc {
178433d6423SLionel Sambuc int funcidx;
179433d6423SLionel Sambuc u64_t res;
180433d6423SLionel Sambuc
181433d6423SLionel Sambuc assert(j == 0);
182433d6423SLionel Sambuc
183433d6423SLionel Sambuc /* loop through the 5 different division functions */
184433d6423SLionel Sambuc for (funcidx = 0; funcidx < 5; funcidx++) {
185433d6423SLionel Sambuc expect_SIGFPE = 1;
186433d6423SLionel Sambuc if (setjmp(jmpbuf_SIGFPE) == 0) {
187433d6423SLionel Sambuc /* divide by zero using various functions */
188433d6423SLionel Sambuc switch (funcidx) {
189433d6423SLionel Sambuc case 0: res = i / j; ERR; break;
190433d6423SLionel Sambuc case 1: res = i / ex64lo(j); ERR; break;
191433d6423SLionel Sambuc case 2: res = i / ex64lo(j); ERR; break;
192433d6423SLionel Sambuc case 3: res = i % j; ERR; break;
193433d6423SLionel Sambuc case 4: res = i % ex64lo(j); ERR; break;
194433d6423SLionel Sambuc default: assert(0); ERR; break;
195433d6423SLionel Sambuc }
196433d6423SLionel Sambuc
197*3083d603SDavid van Moolenbroek do_not_optimize_away((volatile u64_t *)&res);
198*3083d603SDavid van Moolenbroek
199433d6423SLionel Sambuc /* if we reach this point there was no signal and an
200433d6423SLionel Sambuc * error has been recorded
201433d6423SLionel Sambuc */
202433d6423SLionel Sambuc expect_SIGFPE = 0;
203433d6423SLionel Sambuc } else {
204433d6423SLionel Sambuc /* a signal has been received and expect_SIGFPE has
205433d6423SLionel Sambuc * been reset; all is ok now
206433d6423SLionel Sambuc */
207433d6423SLionel Sambuc assert(!expect_SIGFPE);
208433d6423SLionel Sambuc }
209433d6423SLionel Sambuc }
210433d6423SLionel Sambuc }
211433d6423SLionel Sambuc
testdiv(void)212433d6423SLionel Sambuc static void testdiv(void)
213433d6423SLionel Sambuc {
214433d6423SLionel Sambuc u64_t q, r;
215433d6423SLionel Sambuc #if TIMED
216433d6423SLionel Sambuc struct timeval tvstart, tvend;
217433d6423SLionel Sambuc
218433d6423SLionel Sambuc printf("i=0x%.8x%.8x; j=0x%.8x%.8x\n",
219433d6423SLionel Sambuc ex64hi(i), ex64lo(i),
220433d6423SLionel Sambuc ex64hi(j), ex64lo(j));
221433d6423SLionel Sambuc fflush(stdout);
222433d6423SLionel Sambuc if (gettimeofday(&tvstart, NULL) < 0) ERR;
223433d6423SLionel Sambuc #endif
224433d6423SLionel Sambuc
225433d6423SLionel Sambuc /* division by zero has a separate test */
226433d6423SLionel Sambuc if (j == 0) {
227433d6423SLionel Sambuc testdiv0();
228433d6423SLionel Sambuc return;
229433d6423SLionel Sambuc }
230433d6423SLionel Sambuc
231433d6423SLionel Sambuc /* perform division, store q in k to make ERR more informative */
232433d6423SLionel Sambuc q = i / j;
233433d6423SLionel Sambuc r = i % j;
234433d6423SLionel Sambuc k = q;
235433d6423SLionel Sambuc
236433d6423SLionel Sambuc #if TIMED
237433d6423SLionel Sambuc if (gettimeofday(&tvend, NULL) < 0) ERR;
238433d6423SLionel Sambuc tvend.tv_sec -= tvstart.tv_sec;
239433d6423SLionel Sambuc tvend.tv_usec -= tvstart.tv_usec;
240433d6423SLionel Sambuc if (tvend.tv_usec < 0) {
241433d6423SLionel Sambuc tvend.tv_sec -= 1;
242433d6423SLionel Sambuc tvend.tv_usec += 1000000;
243433d6423SLionel Sambuc }
244433d6423SLionel Sambuc printf("q=0x%.8x%.8x; r=0x%.8x%.8x; time=%d.%.6d\n",
245433d6423SLionel Sambuc ex64hi(q), ex64lo(q),
246433d6423SLionel Sambuc ex64hi(r), ex64lo(r),
247433d6423SLionel Sambuc tvend.tv_sec, tvend.tv_usec);
248433d6423SLionel Sambuc fflush(stdout);
249433d6423SLionel Sambuc #endif
250433d6423SLionel Sambuc
251433d6423SLionel Sambuc /* compare to 64/32-bit division if possible */
252433d6423SLionel Sambuc if (!ex64hi(j)) {
253433d6423SLionel Sambuc if (q != i / ex64lo(j)) ERR;
254433d6423SLionel Sambuc if (!ex64hi(q)) {
255433d6423SLionel Sambuc if (q != i / ex64lo(j)) ERR;
256433d6423SLionel Sambuc }
257433d6423SLionel Sambuc if (r != i % ex64lo(j)) ERR;
258433d6423SLionel Sambuc
259433d6423SLionel Sambuc /* compare to 32-bit division if possible */
260433d6423SLionel Sambuc if (!ex64hi(i)) {
261433d6423SLionel Sambuc if (q != ex64lo(i) / ex64lo(j)) ERR;
262433d6423SLionel Sambuc if (r != ex64lo(i) % ex64lo(j)) ERR;
263433d6423SLionel Sambuc }
264433d6423SLionel Sambuc }
265433d6423SLionel Sambuc
266433d6423SLionel Sambuc /* check results using i = q j + r and r < j */
267433d6423SLionel Sambuc if (i != (q * j) + r) ERR;
268433d6423SLionel Sambuc if (r >= j) ERR;
269433d6423SLionel Sambuc }
270433d6423SLionel Sambuc
test(void)271433d6423SLionel Sambuc static void test(void)
272433d6423SLionel Sambuc {
273433d6423SLionel Sambuc int idone, jdone, iidx, jidx;
274433d6423SLionel Sambuc
275433d6423SLionel Sambuc /* loop though all argument value combinations */
276433d6423SLionel Sambuc for (idone = 0, iidx = 0; i = getargval(iidx, &idone), !idone; iidx++)
277433d6423SLionel Sambuc for (jdone = 0, jidx = 0; j = getargval(jidx, &jdone), !jdone; jidx++) {
278433d6423SLionel Sambuc testmul();
279433d6423SLionel Sambuc testdiv();
280433d6423SLionel Sambuc }
281433d6423SLionel Sambuc }
282433d6423SLionel Sambuc
main(void)283433d6423SLionel Sambuc int main(void)
284433d6423SLionel Sambuc {
285433d6423SLionel Sambuc start(53);
286433d6423SLionel Sambuc
287433d6423SLionel Sambuc /* set up signal handler to deal with div by zero */
288433d6423SLionel Sambuc if (setjmp(jmpbuf_main) == 0) {
289433d6423SLionel Sambuc if (signal(SIGFPE, handler_SIGFPE) == SIG_ERR) ERR;
290433d6423SLionel Sambuc
291433d6423SLionel Sambuc /* perform tests */
292433d6423SLionel Sambuc test();
293433d6423SLionel Sambuc } else {
294433d6423SLionel Sambuc /* an unexpected SIGFPE has occurred */
295433d6423SLionel Sambuc ERR;
296433d6423SLionel Sambuc }
297433d6423SLionel Sambuc
298433d6423SLionel Sambuc /* this was all */
299433d6423SLionel Sambuc quit();
300433d6423SLionel Sambuc
301433d6423SLionel Sambuc return(-1); /* Unreachable */
302433d6423SLionel Sambuc }
303