Home | History | Annotate | Download | only in rtest
      1 /*
      2  * main.c
      3  *
      4  * Copyright (c) 1999-2018, Arm Limited.
      5  * SPDX-License-Identifier: MIT
      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 
     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 
     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 
     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 
     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 
    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 
    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