1 /* 2 * main.c 3 * 4 * Copyright (c) 1999-2019, Arm Limited. 5 * SPDX-License-Identifier: MIT OR Apache-2.0 WITH LLVM-exception 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