131914882SAlex Richardson /*
231914882SAlex Richardson  * main.c
331914882SAlex Richardson  *
431914882SAlex Richardson  * Copyright (c) 1999-2019, Arm Limited.
5*072a4ba8SAndrew Turner  * SPDX-License-Identifier: MIT OR Apache-2.0 WITH LLVM-exception
631914882SAlex Richardson  */
731914882SAlex Richardson 
831914882SAlex Richardson #include <assert.h>
931914882SAlex Richardson #include <stdio.h>
1031914882SAlex Richardson #include <string.h>
1131914882SAlex Richardson #include <ctype.h>
1231914882SAlex Richardson #include <stdlib.h>
1331914882SAlex Richardson #include <time.h>
1431914882SAlex Richardson 
1531914882SAlex Richardson #include "intern.h"
1631914882SAlex Richardson 
1731914882SAlex Richardson void gencases(Testable *fn, int number);
1831914882SAlex Richardson void docase(Testable *fn, uint32 *args);
1931914882SAlex Richardson void vet_for_decline(Testable *fn, uint32 *args, uint32 *result, int got_errno_in);
2031914882SAlex Richardson void seed_random(uint32 seed);
2131914882SAlex Richardson 
2231914882SAlex Richardson int check_declines = 0;
2331914882SAlex Richardson int lib_fo = 0;
2431914882SAlex Richardson int lib_no_arith = 0;
2531914882SAlex Richardson int ntests = 0;
2631914882SAlex Richardson 
nargs_(Testable * f)2731914882SAlex Richardson int nargs_(Testable* f) {
2831914882SAlex Richardson     switch((f)->type) {
2931914882SAlex Richardson     case args2:
3031914882SAlex Richardson     case args2f:
3131914882SAlex Richardson     case semi2:
3231914882SAlex Richardson     case semi2f:
3331914882SAlex Richardson     case t_ldexp:
3431914882SAlex Richardson     case t_ldexpf:
3531914882SAlex Richardson     case args1c:
3631914882SAlex Richardson     case args1fc:
3731914882SAlex Richardson     case args1cr:
3831914882SAlex Richardson     case args1fcr:
3931914882SAlex Richardson     case compare:
4031914882SAlex Richardson     case comparef:
4131914882SAlex Richardson         return 2;
4231914882SAlex Richardson     case args2c:
4331914882SAlex Richardson     case args2fc:
4431914882SAlex Richardson         return 4;
4531914882SAlex Richardson     default:
4631914882SAlex Richardson         return 1;
4731914882SAlex Richardson     }
4831914882SAlex Richardson }
4931914882SAlex Richardson 
isdouble(Testable * f)5031914882SAlex Richardson static int isdouble(Testable *f)
5131914882SAlex Richardson {
5231914882SAlex Richardson     switch (f->type) {
5331914882SAlex Richardson       case args1:
5431914882SAlex Richardson       case rred:
5531914882SAlex Richardson       case semi1:
5631914882SAlex Richardson       case t_frexp:
5731914882SAlex Richardson       case t_modf:
5831914882SAlex Richardson       case classify:
5931914882SAlex Richardson       case t_ldexp:
6031914882SAlex Richardson       case args2:
6131914882SAlex Richardson       case semi2:
6231914882SAlex Richardson       case args1c:
6331914882SAlex Richardson       case args1cr:
6431914882SAlex Richardson       case compare:
6531914882SAlex Richardson       case args2c:
6631914882SAlex Richardson         return 1;
6731914882SAlex Richardson       case args1f:
6831914882SAlex Richardson       case rredf:
6931914882SAlex Richardson       case semi1f:
7031914882SAlex Richardson       case t_frexpf:
7131914882SAlex Richardson       case t_modff:
7231914882SAlex Richardson       case classifyf:
7331914882SAlex Richardson       case args2f:
7431914882SAlex Richardson       case semi2f:
7531914882SAlex Richardson       case t_ldexpf:
7631914882SAlex Richardson       case comparef:
7731914882SAlex Richardson       case args1fc:
7831914882SAlex Richardson       case args1fcr:
7931914882SAlex Richardson       case args2fc:
8031914882SAlex Richardson         return 0;
8131914882SAlex Richardson       default:
8231914882SAlex Richardson         assert(0 && "Bad function type");
8331914882SAlex Richardson     }
8431914882SAlex Richardson }
8531914882SAlex Richardson 
find_function(const char * func)8631914882SAlex Richardson Testable *find_function(const char *func)
8731914882SAlex Richardson {
8831914882SAlex Richardson     int i;
8931914882SAlex Richardson     for (i = 0; i < nfunctions; i++) {
9031914882SAlex Richardson         if (func && !strcmp(func, functions[i].name)) {
9131914882SAlex Richardson             return &functions[i];
9231914882SAlex Richardson         }
9331914882SAlex Richardson     }
9431914882SAlex Richardson     return NULL;
9531914882SAlex Richardson }
9631914882SAlex Richardson 
get_operand(const char * str,Testable * f,uint32 * word0,uint32 * word1)9731914882SAlex Richardson void get_operand(const char *str, Testable *f, uint32 *word0, uint32 *word1)
9831914882SAlex Richardson {
9931914882SAlex Richardson     struct special {
10031914882SAlex Richardson         unsigned dblword0, dblword1, sglword;
10131914882SAlex Richardson         const char *name;
10231914882SAlex Richardson     } specials[] = {
10331914882SAlex Richardson         {0x00000000,0x00000000,0x00000000,"0"},
10431914882SAlex Richardson         {0x3FF00000,0x00000000,0x3f800000,"1"},
10531914882SAlex Richardson         {0x7FF00000,0x00000000,0x7f800000,"inf"},
10631914882SAlex Richardson         {0x7FF80000,0x00000001,0x7fc00000,"qnan"},
10731914882SAlex Richardson         {0x7FF00000,0x00000001,0x7f800001,"snan"},
10831914882SAlex Richardson         {0x3ff921fb,0x54442d18,0x3fc90fdb,"pi2"},
10931914882SAlex Richardson         {0x400921fb,0x54442d18,0x40490fdb,"pi"},
11031914882SAlex Richardson         {0x3fe921fb,0x54442d18,0x3f490fdb,"pi4"},
11131914882SAlex Richardson         {0x4002d97c,0x7f3321d2,0x4016cbe4,"3pi4"},
11231914882SAlex Richardson     };
11331914882SAlex Richardson     int i;
11431914882SAlex Richardson 
11531914882SAlex Richardson     for (i = 0; i < (int)(sizeof(specials)/sizeof(*specials)); i++) {
11631914882SAlex Richardson         if (!strcmp(str, specials[i].name) ||
11731914882SAlex Richardson             ((str[0] == '-' || str[0] == '+') &&
11831914882SAlex Richardson              !strcmp(str+1, specials[i].name))) {
11931914882SAlex Richardson             assert(f);
12031914882SAlex Richardson             if (isdouble(f)) {
12131914882SAlex Richardson                 *word0 = specials[i].dblword0;
12231914882SAlex Richardson                 *word1 = specials[i].dblword1;
12331914882SAlex Richardson             } else {
12431914882SAlex Richardson                 *word0 = specials[i].sglword;
12531914882SAlex Richardson                 *word1 = 0;
12631914882SAlex Richardson             }
12731914882SAlex Richardson             if (str[0] == '-')
12831914882SAlex Richardson                 *word0 |= 0x80000000U;
12931914882SAlex Richardson             return;
13031914882SAlex Richardson         }
13131914882SAlex Richardson     }
13231914882SAlex Richardson 
13331914882SAlex Richardson     sscanf(str, "%"I32"x.%"I32"x", word0, word1);
13431914882SAlex Richardson }
13531914882SAlex Richardson 
dofile(FILE * fp,int translating)13631914882SAlex Richardson void dofile(FILE *fp, int translating) {
13731914882SAlex Richardson     char buf[1024], sparebuf[1024], *p;
13831914882SAlex Richardson 
13931914882SAlex Richardson     /*
14031914882SAlex Richardson      * Command syntax is:
14131914882SAlex Richardson      *
14231914882SAlex Richardson      *  - "seed <integer>" sets a random seed
14331914882SAlex Richardson      *
14431914882SAlex Richardson      *  - "test <function> <ntests>" generates random test lines
14531914882SAlex Richardson      *
14631914882SAlex Richardson      *  - "<function> op1=foo [op2=bar]" generates a specific test
14731914882SAlex Richardson      *  - "func=<function> op1=foo [op2=bar]" does the same
14831914882SAlex Richardson      *  - "func=<function> op1=foo result=bar" will just output the line as-is
14931914882SAlex Richardson      *
15031914882SAlex Richardson      *  - a semicolon or a blank line is ignored
15131914882SAlex Richardson      */
15231914882SAlex Richardson     while (fgets(buf, sizeof(buf), fp)) {
15331914882SAlex Richardson         buf[strcspn(buf, "\r\n")] = '\0';
15431914882SAlex Richardson         strcpy(sparebuf, buf);
15531914882SAlex Richardson         p = buf;
15631914882SAlex Richardson         while (*p && isspace(*p)) p++;
15731914882SAlex Richardson         if (!*p || *p == ';') {
15831914882SAlex Richardson             /* Comment or blank line. Only print if `translating' is set. */
15931914882SAlex Richardson             if (translating)
16031914882SAlex Richardson                 printf("%s\n", buf);
16131914882SAlex Richardson             continue;
16231914882SAlex Richardson         }
16331914882SAlex Richardson         if (!strncmp(buf, "seed ", 5)) {
16431914882SAlex Richardson             seed_random(atoi(buf+5));
16531914882SAlex Richardson         } else if (!strncmp(buf, "random=", 7)) {
16631914882SAlex Richardson             /*
16731914882SAlex Richardson              * Copy 'random=on' / 'random=off' lines unconditionally
16831914882SAlex Richardson              * to the output, so that random test failures can be
16931914882SAlex Richardson              * accumulated into a recent-failures-list file and
17031914882SAlex Richardson              * still identified as random-in-origin when re-run the
17131914882SAlex Richardson              * next day.
17231914882SAlex Richardson              */
17331914882SAlex Richardson             printf("%s\n", buf);
17431914882SAlex Richardson         } else if (!strncmp(buf, "test ", 5)) {
17531914882SAlex Richardson             char *p = buf+5;
17631914882SAlex Richardson             char *q;
17731914882SAlex Richardson             int ntests, i;
17831914882SAlex Richardson             q = p;
17931914882SAlex Richardson             while (*p && !isspace(*p)) p++;
18031914882SAlex Richardson             if (*p) *p++ = '\0';
18131914882SAlex Richardson             while (*p && isspace(*p)) p++;
18231914882SAlex Richardson             if (*p)
18331914882SAlex Richardson                 ntests = atoi(p);
18431914882SAlex Richardson             else
18531914882SAlex Richardson                 ntests = 100;          /* *shrug* */
18631914882SAlex Richardson             for (i = 0; i < nfunctions; i++) {
18731914882SAlex Richardson                 if (!strcmp(q, functions[i].name)) {
18831914882SAlex Richardson                     gencases(&functions[i], ntests);
18931914882SAlex Richardson                     break;
19031914882SAlex Richardson                 }
19131914882SAlex Richardson             }
19231914882SAlex Richardson             if (i == nfunctions) {
19331914882SAlex Richardson                 fprintf(stderr, "unknown test `%s'\n", q);
19431914882SAlex Richardson             }
19531914882SAlex Richardson         } else {
19631914882SAlex Richardson             /*
19731914882SAlex Richardson              * Parse a specific test line.
19831914882SAlex Richardson              */
19931914882SAlex Richardson             uint32 ops[8], result[8];
20031914882SAlex Richardson             int got_op = 0; /* &1 for got_op1, &4 for got_op3 etc. */
20131914882SAlex Richardson             Testable *f = 0;
20231914882SAlex Richardson             char *q, *r;
20331914882SAlex Richardson             int got_result = 0, got_errno_in = 0;
20431914882SAlex Richardson 
20531914882SAlex Richardson             for (q = strtok(p, " \t"); q; q = strtok(NULL, " \t")) {
20631914882SAlex Richardson                 r = strchr(q, '=');
20731914882SAlex Richardson                 if (!r) {
20831914882SAlex Richardson                     f = find_function(q);
20931914882SAlex Richardson                 } else {
21031914882SAlex Richardson                     *r++ = '\0';
21131914882SAlex Richardson 
21231914882SAlex Richardson                     if (!strcmp(q, "func"))
21331914882SAlex Richardson                         f = find_function(r);
21431914882SAlex Richardson                     else if (!strcmp(q, "op1") || !strcmp(q, "op1r")) {
21531914882SAlex Richardson                         get_operand(r, f, &ops[0], &ops[1]);
21631914882SAlex Richardson                         got_op |= 1;
21731914882SAlex Richardson                     } else if (!strcmp(q, "op2") || !strcmp(q, "op1i")) {
21831914882SAlex Richardson                         get_operand(r, f, &ops[2], &ops[3]);
21931914882SAlex Richardson                         got_op |= 2;
22031914882SAlex Richardson                     } else if (!strcmp(q, "op2r")) {
22131914882SAlex Richardson                         get_operand(r, f, &ops[4], &ops[5]);
22231914882SAlex Richardson                         got_op |= 4;
22331914882SAlex Richardson                     } else if (!strcmp(q, "op2i")) {
22431914882SAlex Richardson                         get_operand(r, f, &ops[6], &ops[7]);
22531914882SAlex Richardson                         got_op |= 8;
22631914882SAlex Richardson                     } else if (!strcmp(q, "result") || !strcmp(q, "resultr")) {
22731914882SAlex Richardson                         get_operand(r, f, &result[0], &result[1]);
22831914882SAlex Richardson                         got_result |= 1;
22931914882SAlex Richardson                     } else if (!strcmp(q, "resulti")) {
23031914882SAlex Richardson                         get_operand(r, f, &result[4], &result[5]);
23131914882SAlex Richardson                         got_result |= 2;
23231914882SAlex Richardson                     } else if (!strcmp(q, "res2")) {
23331914882SAlex Richardson                         get_operand(r, f, &result[2], &result[3]);
23431914882SAlex Richardson                         got_result |= 4;
23531914882SAlex Richardson                     } else if (!strcmp(q, "errno_in")) {
23631914882SAlex Richardson                         got_errno_in = 1;
23731914882SAlex Richardson                     }
23831914882SAlex Richardson                 }
23931914882SAlex Richardson             }
24031914882SAlex Richardson 
24131914882SAlex Richardson             /*
24231914882SAlex Richardson              * Test cases already set up by the input are not
24331914882SAlex Richardson              * reprocessed by default, unlike the fplib tests. (This
24431914882SAlex Richardson              * is mostly for historical reasons, because we used to
24531914882SAlex Richardson              * use a very slow and incomplete internal reference
24631914882SAlex Richardson              * implementation; now our ref impl is MPFR/MPC it
24731914882SAlex Richardson              * probably wouldn't be such a bad idea, though we'd still
24831914882SAlex Richardson              * have to make sure all the special cases came out
24931914882SAlex Richardson              * right.) If translating==2 (corresponding to the -T
25031914882SAlex Richardson              * command-line option) then we regenerate everything
25131914882SAlex Richardson              * regardless.
25231914882SAlex Richardson              */
25331914882SAlex Richardson             if (got_result && translating < 2) {
25431914882SAlex Richardson                 if (f)
25531914882SAlex Richardson                     vet_for_decline(f, ops, result, got_errno_in);
25631914882SAlex Richardson                 puts(sparebuf);
25731914882SAlex Richardson                 continue;
25831914882SAlex Richardson             }
25931914882SAlex Richardson 
26031914882SAlex Richardson             if (f && got_op==(1<<nargs_(f))-1) {
26131914882SAlex Richardson                 /*
26231914882SAlex Richardson                  * And do it!
26331914882SAlex Richardson                  */
26431914882SAlex Richardson                 docase(f, ops);
26531914882SAlex Richardson             }
26631914882SAlex Richardson         }
26731914882SAlex Richardson     }
26831914882SAlex Richardson }
26931914882SAlex Richardson 
main(int argc,char ** argv)27031914882SAlex Richardson int main(int argc, char **argv) {
27131914882SAlex Richardson     int errs = 0, opts = 1, files = 0, translating = 0;
27231914882SAlex Richardson     unsigned int seed = 1; /* in case no explicit seed provided */
27331914882SAlex Richardson 
27431914882SAlex Richardson     seed_random(seed);
27531914882SAlex Richardson 
27631914882SAlex Richardson     setvbuf(stdout, NULL, _IOLBF, BUFSIZ); /* stops incomplete lines being printed when out of time */
27731914882SAlex Richardson 
27831914882SAlex Richardson     while (--argc) {
27931914882SAlex Richardson         FILE *fp;
28031914882SAlex Richardson         char *p = *++argv;
28131914882SAlex Richardson 
28231914882SAlex Richardson         if (opts && *p == '-') {
28331914882SAlex Richardson             if(*(p+1) == 0) { /* single -, read from stdin */
28431914882SAlex Richardson                 break;
28531914882SAlex Richardson             } else if (!strcmp(p, "-t")) {
28631914882SAlex Richardson                 translating = 1;
28731914882SAlex Richardson             } else if (!strcmp(p, "-T")) {
28831914882SAlex Richardson                 translating = 2;
28931914882SAlex Richardson             } else if (!strcmp(p, "-c")) {
29031914882SAlex Richardson                 check_declines = 1;
29131914882SAlex Richardson             } else if (!strcmp(p, "--")) {
29231914882SAlex Richardson                 opts = 0;
29331914882SAlex Richardson             } else if (!strcmp(p,"--seed") && argc > 1 && 1==sscanf(*(argv+1),"%u",&seed)) {
29431914882SAlex Richardson                 seed_random(seed);
29531914882SAlex Richardson                 argv++; /* next in argv is seed value, so skip */
29631914882SAlex Richardson                 --argc;
29731914882SAlex Richardson             } else if (!strcmp(p, "-fo")) {
29831914882SAlex Richardson                 lib_fo = 1;
29931914882SAlex Richardson             } else if (!strcmp(p, "-noarith")) {
30031914882SAlex Richardson                 lib_no_arith = 1;
30131914882SAlex Richardson             } else {
30231914882SAlex Richardson                 fprintf(stderr,
30331914882SAlex Richardson                         "rtest: ignoring unrecognised option '%s'\n", p);
30431914882SAlex Richardson                 errs = 1;
30531914882SAlex Richardson             }
30631914882SAlex Richardson         } else {
30731914882SAlex Richardson             files = 1;
30831914882SAlex Richardson             if (!errs) {
30931914882SAlex Richardson                 fp = fopen(p, "r");
31031914882SAlex Richardson                 if (fp) {
31131914882SAlex Richardson                     dofile(fp, translating);
31231914882SAlex Richardson                     fclose(fp);
31331914882SAlex Richardson                 } else {
31431914882SAlex Richardson                     perror(p);
31531914882SAlex Richardson                     errs = 1;
31631914882SAlex Richardson                 }
31731914882SAlex Richardson             }
31831914882SAlex Richardson         }
31931914882SAlex Richardson     }
32031914882SAlex Richardson 
32131914882SAlex Richardson     /*
32231914882SAlex Richardson      * If no filename arguments, use stdin.
32331914882SAlex Richardson      */
32431914882SAlex Richardson     if (!files && !errs) {
32531914882SAlex Richardson         dofile(stdin, translating);
32631914882SAlex Richardson     }
32731914882SAlex Richardson 
32831914882SAlex Richardson     if (check_declines) {
32931914882SAlex Richardson         fprintf(stderr, "Tests expected to run: %d\n", ntests);
33031914882SAlex Richardson         fflush(stderr);
33131914882SAlex Richardson     }
33231914882SAlex Richardson 
33331914882SAlex Richardson     return errs;
33431914882SAlex Richardson }
335