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