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