/* * main.c * * Copyright (c) 1999-2019, Arm Limited. * SPDX-License-Identifier: MIT */ #include #include #include #include #include #include #include "intern.h" void gencases(Testable *fn, int number); void docase(Testable *fn, uint32 *args); void vet_for_decline(Testable *fn, uint32 *args, uint32 *result, int got_errno_in); void seed_random(uint32 seed); int check_declines = 0; int lib_fo = 0; int lib_no_arith = 0; int ntests = 0; int nargs_(Testable* f) { switch((f)->type) { case args2: case args2f: case semi2: case semi2f: case t_ldexp: case t_ldexpf: case args1c: case args1fc: case args1cr: case args1fcr: case compare: case comparef: return 2; case args2c: case args2fc: return 4; default: return 1; } } static int isdouble(Testable *f) { switch (f->type) { case args1: case rred: case semi1: case t_frexp: case t_modf: case classify: case t_ldexp: case args2: case semi2: case args1c: case args1cr: case compare: case args2c: return 1; case args1f: case rredf: case semi1f: case t_frexpf: case t_modff: case classifyf: case args2f: case semi2f: case t_ldexpf: case comparef: case args1fc: case args1fcr: case args2fc: return 0; default: assert(0 && "Bad function type"); } } Testable *find_function(const char *func) { int i; for (i = 0; i < nfunctions; i++) { if (func && !strcmp(func, functions[i].name)) { return &functions[i]; } } return NULL; } void get_operand(const char *str, Testable *f, uint32 *word0, uint32 *word1) { struct special { unsigned dblword0, dblword1, sglword; const char *name; } specials[] = { {0x00000000,0x00000000,0x00000000,"0"}, {0x3FF00000,0x00000000,0x3f800000,"1"}, {0x7FF00000,0x00000000,0x7f800000,"inf"}, {0x7FF80000,0x00000001,0x7fc00000,"qnan"}, {0x7FF00000,0x00000001,0x7f800001,"snan"}, {0x3ff921fb,0x54442d18,0x3fc90fdb,"pi2"}, {0x400921fb,0x54442d18,0x40490fdb,"pi"}, {0x3fe921fb,0x54442d18,0x3f490fdb,"pi4"}, {0x4002d97c,0x7f3321d2,0x4016cbe4,"3pi4"}, }; int i; for (i = 0; i < (int)(sizeof(specials)/sizeof(*specials)); i++) { if (!strcmp(str, specials[i].name) || ((str[0] == '-' || str[0] == '+') && !strcmp(str+1, specials[i].name))) { assert(f); if (isdouble(f)) { *word0 = specials[i].dblword0; *word1 = specials[i].dblword1; } else { *word0 = specials[i].sglword; *word1 = 0; } if (str[0] == '-') *word0 |= 0x80000000U; return; } } sscanf(str, "%"I32"x.%"I32"x", word0, word1); } void dofile(FILE *fp, int translating) { char buf[1024], sparebuf[1024], *p; /* * Command syntax is: * * - "seed " sets a random seed * * - "test " generates random test lines * * - " op1=foo [op2=bar]" generates a specific test * - "func= op1=foo [op2=bar]" does the same * - "func= op1=foo result=bar" will just output the line as-is * * - a semicolon or a blank line is ignored */ while (fgets(buf, sizeof(buf), fp)) { buf[strcspn(buf, "\r\n")] = '\0'; strcpy(sparebuf, buf); p = buf; while (*p && isspace(*p)) p++; if (!*p || *p == ';') { /* Comment or blank line. Only print if `translating' is set. */ if (translating) printf("%s\n", buf); continue; } if (!strncmp(buf, "seed ", 5)) { seed_random(atoi(buf+5)); } else if (!strncmp(buf, "random=", 7)) { /* * Copy 'random=on' / 'random=off' lines unconditionally * to the output, so that random test failures can be * accumulated into a recent-failures-list file and * still identified as random-in-origin when re-run the * next day. */ printf("%s\n", buf); } else if (!strncmp(buf, "test ", 5)) { char *p = buf+5; char *q; int ntests, i; q = p; while (*p && !isspace(*p)) p++; if (*p) *p++ = '\0'; while (*p && isspace(*p)) p++; if (*p) ntests = atoi(p); else ntests = 100; /* *shrug* */ for (i = 0; i < nfunctions; i++) { if (!strcmp(q, functions[i].name)) { gencases(&functions[i], ntests); break; } } if (i == nfunctions) { fprintf(stderr, "unknown test `%s'\n", q); } } else { /* * Parse a specific test line. */ uint32 ops[8], result[8]; int got_op = 0; /* &1 for got_op1, &4 for got_op3 etc. */ Testable *f = 0; char *q, *r; int got_result = 0, got_errno_in = 0; for (q = strtok(p, " \t"); q; q = strtok(NULL, " \t")) { r = strchr(q, '='); if (!r) { f = find_function(q); } else { *r++ = '\0'; if (!strcmp(q, "func")) f = find_function(r); else if (!strcmp(q, "op1") || !strcmp(q, "op1r")) { get_operand(r, f, &ops[0], &ops[1]); got_op |= 1; } else if (!strcmp(q, "op2") || !strcmp(q, "op1i")) { get_operand(r, f, &ops[2], &ops[3]); got_op |= 2; } else if (!strcmp(q, "op2r")) { get_operand(r, f, &ops[4], &ops[5]); got_op |= 4; } else if (!strcmp(q, "op2i")) { get_operand(r, f, &ops[6], &ops[7]); got_op |= 8; } else if (!strcmp(q, "result") || !strcmp(q, "resultr")) { get_operand(r, f, &result[0], &result[1]); got_result |= 1; } else if (!strcmp(q, "resulti")) { get_operand(r, f, &result[4], &result[5]); got_result |= 2; } else if (!strcmp(q, "res2")) { get_operand(r, f, &result[2], &result[3]); got_result |= 4; } else if (!strcmp(q, "errno_in")) { got_errno_in = 1; } } } /* * Test cases already set up by the input are not * reprocessed by default, unlike the fplib tests. (This * is mostly for historical reasons, because we used to * use a very slow and incomplete internal reference * implementation; now our ref impl is MPFR/MPC it * probably wouldn't be such a bad idea, though we'd still * have to make sure all the special cases came out * right.) If translating==2 (corresponding to the -T * command-line option) then we regenerate everything * regardless. */ if (got_result && translating < 2) { if (f) vet_for_decline(f, ops, result, got_errno_in); puts(sparebuf); continue; } if (f && got_op==(1< 1 && 1==sscanf(*(argv+1),"%u",&seed)) { seed_random(seed); argv++; /* next in argv is seed value, so skip */ --argc; } else if (!strcmp(p, "-fo")) { lib_fo = 1; } else if (!strcmp(p, "-noarith")) { lib_no_arith = 1; } else { fprintf(stderr, "rtest: ignoring unrecognised option '%s'\n", p); errs = 1; } } else { files = 1; if (!errs) { fp = fopen(p, "r"); if (fp) { dofile(fp, translating); fclose(fp); } else { perror(p); errs = 1; } } } } /* * If no filename arguments, use stdin. */ if (!files && !errs) { dofile(stdin, translating); } if (check_declines) { fprintf(stderr, "Tests expected to run: %d\n", ntests); fflush(stderr); } return errs; }