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