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