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
18void gencases(Testable *fn, int number);
19void docase(Testable *fn, uint32 *args);
20void vet_for_decline(Testable *fn, uint32 *args, uint32 *result, int got_errno_in);
21void seed_random(uint32 seed);
22
23int check_declines = 0;
24int lib_fo = 0;
25int lib_no_arith = 0;
26int ntests = 0;
27
28int 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
51static 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
87Testable *find_function(const char *func)
88{
89 int i;
90 for (i = 0; i < nfunctions; i++) {
91 if (func && !strcmp(s1: func, s2: functions[i].name)) {
92 return &functions[i];
93 }
94 }
95 return NULL;
96}
97
98void 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(s1: str, s2: specials[i].name) ||
118 ((str[0] == '-' || str[0] == '+') &&
119 !strcmp(s1: str+1, s2: 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(s: str, format: "%"I32"x.%"I32"x", word0, word1);
135}
136
137void 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(s: buf, n: sizeof(buf), stream: fp)) {
154 buf[strcspn(s: buf, reject: "\r\n")] = '\0';
155 strcpy(dest: sparebuf, src: 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(format: "%s\n", buf);
162 continue;
163 }
164 if (!strncmp(s1: buf, s2: "seed ", n: 5)) {
165 seed_random(seed: atoi(nptr: buf+5));
166 } else if (!strncmp(s1: buf, s2: "random=", n: 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(format: "%s\n", buf);
175 } else if (!strncmp(s1: buf, s2: "test ", n: 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(nptr: p);
185 else
186 ntests = 100; /* *shrug* */
187 for (i = 0; i < nfunctions; i++) {
188 if (!strcmp(s1: q, s2: functions[i].name)) {
189 gencases(fn: &functions[i], number: ntests);
190 break;
191 }
192 }
193 if (i == nfunctions) {
194 fprintf(stderr, format: "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(s: p, delim: " \t"); q; q = strtok(NULL, delim: " \t")) {
207 r = strchr(s: q, c: '=');
208 if (!r) {
209 f = find_function(func: q);
210 } else {
211 *r++ = '\0';
212
213 if (!strcmp(s1: q, s2: "func"))
214 f = find_function(func: r);
215 else if (!strcmp(s1: q, s2: "op1") || !strcmp(s1: q, s2: "op1r")) {
216 get_operand(str: r, f, word0: &ops[0], word1: &ops[1]);
217 got_op |= 1;
218 } else if (!strcmp(s1: q, s2: "op2") || !strcmp(s1: q, s2: "op1i")) {
219 get_operand(str: r, f, word0: &ops[2], word1: &ops[3]);
220 got_op |= 2;
221 } else if (!strcmp(s1: q, s2: "op2r")) {
222 get_operand(str: r, f, word0: &ops[4], word1: &ops[5]);
223 got_op |= 4;
224 } else if (!strcmp(s1: q, s2: "op2i")) {
225 get_operand(str: r, f, word0: &ops[6], word1: &ops[7]);
226 got_op |= 8;
227 } else if (!strcmp(s1: q, s2: "result") || !strcmp(s1: q, s2: "resultr")) {
228 get_operand(str: r, f, word0: &result[0], word1: &result[1]);
229 got_result |= 1;
230 } else if (!strcmp(s1: q, s2: "resulti")) {
231 get_operand(str: r, f, word0: &result[4], word1: &result[5]);
232 got_result |= 2;
233 } else if (!strcmp(s1: q, s2: "res2")) {
234 get_operand(str: r, f, word0: &result[2], word1: &result[3]);
235 got_result |= 4;
236 } else if (!strcmp(s1: q, s2: "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(fn: f, args: ops, result, got_errno_in);
257 puts(s: sparebuf);
258 continue;
259 }
260
261 if (f && got_op==(1<<nargs_(f))-1) {
262 /*
263 * And do it!
264 */
265 docase(fn: f, args: ops);
266 }
267 }
268 }
269}
270
271int 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(s1: p, s2: "-t")) {
287 translating = 1;
288 } else if (!strcmp(s1: p, s2: "-T")) {
289 translating = 2;
290 } else if (!strcmp(s1: p, s2: "-c")) {
291 check_declines = 1;
292 } else if (!strcmp(s1: p, s2: "--")) {
293 opts = 0;
294 } else if (!strcmp(s1: p,s2: "--seed") && argc > 1 && 1==sscanf(s: *(argv+1),format: "%u",&seed)) {
295 seed_random(seed);
296 argv++; /* next in argv is seed value, so skip */
297 --argc;
298 } else if (!strcmp(s1: p, s2: "-fo")) {
299 lib_fo = 1;
300 } else if (!strcmp(s1: p, s2: "-noarith")) {
301 lib_no_arith = 1;
302 } else {
303 fprintf(stderr,
304 format: "rtest: ignoring unrecognised option '%s'\n", p);
305 errs = 1;
306 }
307 } else {
308 files = 1;
309 if (!errs) {
310 fp = fopen(filename: p, modes: "r");
311 if (fp) {
312 dofile(fp, translating);
313 fclose(stream: fp);
314 } else {
315 perror(s: 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, format: "Tests expected to run: %d\n", ntests);
331 fflush(stderr);
332 }
333
334 return errs;
335}
336

source code of libc/AOR_v20.02/math/test/rtest/main.c