1 /*
2  * main.c
3  *
4  * Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5  * See https://llvm.org/LICENSE.txt for license information.
6  * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7  */
8 
9 #include <assert.h>
10 #include <stdio.h>
11 #include <string.h>
12 #include <ctype.h>
13 #include <stdlib.h>
14 #include <time.h>
15 
16 #include "intern.h"
17 
18 void gencases(Testable *fn, int number);
19 void docase(Testable *fn, uint32 *args);
20 void vet_for_decline(Testable *fn, uint32 *args, uint32 *result, int got_errno_in);
21 void seed_random(uint32 seed);
22 
23 int check_declines = 0;
24 int lib_fo = 0;
25 int lib_no_arith = 0;
26 int ntests = 0;
27 
nargs_(Testable * f)28 int nargs_(Testable* f) {
29     switch((f)->type) {
30     case args2:
31     case args2f:
32     case semi2:
33     case semi2f:
34     case t_ldexp:
35     case t_ldexpf:
36     case args1c:
37     case args1fc:
38     case args1cr:
39     case args1fcr:
40     case compare:
41     case comparef:
42         return 2;
43     case args2c:
44     case args2fc:
45         return 4;
46     default:
47         return 1;
48     }
49 }
50 
isdouble(Testable * f)51 static int isdouble(Testable *f)
52 {
53     switch (f->type) {
54       case args1:
55       case rred:
56       case semi1:
57       case t_frexp:
58       case t_modf:
59       case classify:
60       case t_ldexp:
61       case args2:
62       case semi2:
63       case args1c:
64       case args1cr:
65       case compare:
66       case args2c:
67         return 1;
68       case args1f:
69       case rredf:
70       case semi1f:
71       case t_frexpf:
72       case t_modff:
73       case classifyf:
74       case args2f:
75       case semi2f:
76       case t_ldexpf:
77       case comparef:
78       case args1fc:
79       case args1fcr:
80       case args2fc:
81         return 0;
82       default:
83         assert(0 && "Bad function type");
84     }
85 }
86 
find_function(const char * func)87 Testable *find_function(const char *func)
88 {
89     int i;
90     for (i = 0; i < nfunctions; i++) {
91         if (func && !strcmp(func, functions[i].name)) {
92             return &functions[i];
93         }
94     }
95     return NULL;
96 }
97 
get_operand(const char * str,Testable * f,uint32 * word0,uint32 * word1)98 void get_operand(const char *str, Testable *f, uint32 *word0, uint32 *word1)
99 {
100     struct special {
101         unsigned dblword0, dblword1, sglword;
102         const char *name;
103     } specials[] = {
104         {0x00000000,0x00000000,0x00000000,"0"},
105         {0x3FF00000,0x00000000,0x3f800000,"1"},
106         {0x7FF00000,0x00000000,0x7f800000,"inf"},
107         {0x7FF80000,0x00000001,0x7fc00000,"qnan"},
108         {0x7FF00000,0x00000001,0x7f800001,"snan"},
109         {0x3ff921fb,0x54442d18,0x3fc90fdb,"pi2"},
110         {0x400921fb,0x54442d18,0x40490fdb,"pi"},
111         {0x3fe921fb,0x54442d18,0x3f490fdb,"pi4"},
112         {0x4002d97c,0x7f3321d2,0x4016cbe4,"3pi4"},
113     };
114     int i;
115 
116     for (i = 0; i < (int)(sizeof(specials)/sizeof(*specials)); i++) {
117         if (!strcmp(str, specials[i].name) ||
118             ((str[0] == '-' || str[0] == '+') &&
119              !strcmp(str+1, specials[i].name))) {
120             assert(f);
121             if (isdouble(f)) {
122                 *word0 = specials[i].dblword0;
123                 *word1 = specials[i].dblword1;
124             } else {
125                 *word0 = specials[i].sglword;
126                 *word1 = 0;
127             }
128             if (str[0] == '-')
129                 *word0 |= 0x80000000U;
130             return;
131         }
132     }
133 
134     sscanf(str, "%"I32"x.%"I32"x", word0, word1);
135 }
136 
dofile(FILE * fp,int translating)137 void dofile(FILE *fp, int translating) {
138     char buf[1024], sparebuf[1024], *p;
139 
140     /*
141      * Command syntax is:
142      *
143      *  - "seed <integer>" sets a random seed
144      *
145      *  - "test <function> <ntests>" generates random test lines
146      *
147      *  - "<function> op1=foo [op2=bar]" generates a specific test
148      *  - "func=<function> op1=foo [op2=bar]" does the same
149      *  - "func=<function> op1=foo result=bar" will just output the line as-is
150      *
151      *  - a semicolon or a blank line is ignored
152      */
153     while (fgets(buf, sizeof(buf), fp)) {
154         buf[strcspn(buf, "\r\n")] = '\0';
155         strcpy(sparebuf, buf);
156         p = buf;
157         while (*p && isspace(*p)) p++;
158         if (!*p || *p == ';') {
159             /* Comment or blank line. Only print if `translating' is set. */
160             if (translating)
161                 printf("%s\n", buf);
162             continue;
163         }
164         if (!strncmp(buf, "seed ", 5)) {
165             seed_random(atoi(buf+5));
166         } else if (!strncmp(buf, "random=", 7)) {
167             /*
168              * Copy 'random=on' / 'random=off' lines unconditionally
169              * to the output, so that random test failures can be
170              * accumulated into a recent-failures-list file and
171              * still identified as random-in-origin when re-run the
172              * next day.
173              */
174             printf("%s\n", buf);
175         } else if (!strncmp(buf, "test ", 5)) {
176             char *p = buf+5;
177             char *q;
178             int ntests, i;
179             q = p;
180             while (*p && !isspace(*p)) p++;
181             if (*p) *p++ = '\0';
182             while (*p && isspace(*p)) p++;
183             if (*p)
184                 ntests = atoi(p);
185             else
186                 ntests = 100;          /* *shrug* */
187             for (i = 0; i < nfunctions; i++) {
188                 if (!strcmp(q, functions[i].name)) {
189                     gencases(&functions[i], ntests);
190                     break;
191                 }
192             }
193             if (i == nfunctions) {
194                 fprintf(stderr, "unknown test `%s'\n", q);
195             }
196         } else {
197             /*
198              * Parse a specific test line.
199              */
200             uint32 ops[8], result[8];
201             int got_op = 0; /* &1 for got_op1, &4 for got_op3 etc. */
202             Testable *f = 0;
203             char *q, *r;
204             int got_result = 0, got_errno_in = 0;
205 
206             for (q = strtok(p, " \t"); q; q = strtok(NULL, " \t")) {
207                 r = strchr(q, '=');
208                 if (!r) {
209                     f = find_function(q);
210                 } else {
211                     *r++ = '\0';
212 
213                     if (!strcmp(q, "func"))
214                         f = find_function(r);
215                     else if (!strcmp(q, "op1") || !strcmp(q, "op1r")) {
216                         get_operand(r, f, &ops[0], &ops[1]);
217                         got_op |= 1;
218                     } else if (!strcmp(q, "op2") || !strcmp(q, "op1i")) {
219                         get_operand(r, f, &ops[2], &ops[3]);
220                         got_op |= 2;
221                     } else if (!strcmp(q, "op2r")) {
222                         get_operand(r, f, &ops[4], &ops[5]);
223                         got_op |= 4;
224                     } else if (!strcmp(q, "op2i")) {
225                         get_operand(r, f, &ops[6], &ops[7]);
226                         got_op |= 8;
227                     } else if (!strcmp(q, "result") || !strcmp(q, "resultr")) {
228                         get_operand(r, f, &result[0], &result[1]);
229                         got_result |= 1;
230                     } else if (!strcmp(q, "resulti")) {
231                         get_operand(r, f, &result[4], &result[5]);
232                         got_result |= 2;
233                     } else if (!strcmp(q, "res2")) {
234                         get_operand(r, f, &result[2], &result[3]);
235                         got_result |= 4;
236                     } else if (!strcmp(q, "errno_in")) {
237                         got_errno_in = 1;
238                     }
239                 }
240             }
241 
242             /*
243              * Test cases already set up by the input are not
244              * reprocessed by default, unlike the fplib tests. (This
245              * is mostly for historical reasons, because we used to
246              * use a very slow and incomplete internal reference
247              * implementation; now our ref impl is MPFR/MPC it
248              * probably wouldn't be such a bad idea, though we'd still
249              * have to make sure all the special cases came out
250              * right.) If translating==2 (corresponding to the -T
251              * command-line option) then we regenerate everything
252              * regardless.
253              */
254             if (got_result && translating < 2) {
255                 if (f)
256                     vet_for_decline(f, ops, result, got_errno_in);
257                 puts(sparebuf);
258                 continue;
259             }
260 
261             if (f && got_op==(1<<nargs_(f))-1) {
262                 /*
263                  * And do it!
264                  */
265                 docase(f, ops);
266             }
267         }
268     }
269 }
270 
main(int argc,char ** argv)271 int main(int argc, char **argv) {
272     int errs = 0, opts = 1, files = 0, translating = 0;
273     unsigned int seed = 1; /* in case no explicit seed provided */
274 
275     seed_random(seed);
276 
277     setvbuf(stdout, NULL, _IOLBF, BUFSIZ); /* stops incomplete lines being printed when out of time */
278 
279     while (--argc) {
280         FILE *fp;
281         char *p = *++argv;
282 
283         if (opts && *p == '-') {
284             if(*(p+1) == 0) { /* single -, read from stdin */
285                 break;
286             } else if (!strcmp(p, "-t")) {
287                 translating = 1;
288             } else if (!strcmp(p, "-T")) {
289                 translating = 2;
290             } else if (!strcmp(p, "-c")) {
291                 check_declines = 1;
292             } else if (!strcmp(p, "--")) {
293                 opts = 0;
294             } else if (!strcmp(p,"--seed") && argc > 1 && 1==sscanf(*(argv+1),"%u",&seed)) {
295                 seed_random(seed);
296                 argv++; /* next in argv is seed value, so skip */
297                 --argc;
298             } else if (!strcmp(p, "-fo")) {
299                 lib_fo = 1;
300             } else if (!strcmp(p, "-noarith")) {
301                 lib_no_arith = 1;
302             } else {
303                 fprintf(stderr,
304                         "rtest: ignoring unrecognised option '%s'\n", p);
305                 errs = 1;
306             }
307         } else {
308             files = 1;
309             if (!errs) {
310                 fp = fopen(p, "r");
311                 if (fp) {
312                     dofile(fp, translating);
313                     fclose(fp);
314                 } else {
315                     perror(p);
316                     errs = 1;
317                 }
318             }
319         }
320     }
321 
322     /*
323      * If no filename arguments, use stdin.
324      */
325     if (!files && !errs) {
326         dofile(stdin, translating);
327     }
328 
329     if (check_declines) {
330         fprintf(stderr, "Tests expected to run: %d\n", ntests);
331         fflush(stderr);
332     }
333 
334     return errs;
335 }
336