annotate src/fftw-3.3.5/kernel/planner.c @ 54:5f67a29f0fc7

Rebuild MAD with 64-bit FPM
author Chris Cannam <cannam@all-day-breakfast.com>
date Wed, 30 Nov 2016 20:59:17 +0000
parents 2cd0e3b3e1fd
children
rev   line source
Chris@42 1 /*
Chris@42 2 * Copyright (c) 2000 Matteo Frigo
Chris@42 3 * Copyright (c) 2000 Massachusetts Institute of Technology
Chris@42 4 *
Chris@42 5 * This program is free software; you can redistribute it and/or modify
Chris@42 6 * it under the terms of the GNU General Public License as published by
Chris@42 7 * the Free Software Foundation; either version 2 of the License, or
Chris@42 8 * (at your option) any later version.
Chris@42 9 *
Chris@42 10 * This program is distributed in the hope that it will be useful,
Chris@42 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Chris@42 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Chris@42 13 * GNU General Public License for more details.
Chris@42 14 *
Chris@42 15 * You should have received a copy of the GNU General Public License
Chris@42 16 * along with this program; if not, write to the Free Software
Chris@42 17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Chris@42 18 *
Chris@42 19 */
Chris@42 20
Chris@42 21 #include "ifftw.h"
Chris@42 22 #include <string.h>
Chris@42 23
Chris@42 24 /* GNU Coding Standards, Sec. 5.2: "Please write the comments in a GNU
Chris@42 25 program in English, because English is the one language that nearly
Chris@42 26 all programmers in all countries can read."
Chris@42 27
Chris@42 28 ingemisco tanquam reus
Chris@42 29 culpa rubet vultus meus
Chris@42 30 supplicanti parce [rms]
Chris@42 31 */
Chris@42 32
Chris@42 33 #define VALIDP(solution) ((solution)->flags.hash_info & H_VALID)
Chris@42 34 #define LIVEP(solution) ((solution)->flags.hash_info & H_LIVE)
Chris@42 35 #define SLVNDX(solution) ((solution)->flags.slvndx)
Chris@42 36 #define BLISS(flags) (((flags).hash_info) & BLESSING)
Chris@42 37 #define INFEASIBLE_SLVNDX ((1U<<BITS_FOR_SLVNDX)-1)
Chris@42 38
Chris@42 39
Chris@42 40 #define MAXNAM 64 /* maximum length of registrar's name.
Chris@42 41 Used for reading wisdom. There is no point
Chris@42 42 in doing this right */
Chris@42 43
Chris@42 44
Chris@42 45 #ifdef FFTW_DEBUG
Chris@42 46 static void check(hashtab *ht);
Chris@42 47 #endif
Chris@42 48
Chris@42 49 /* x <= y */
Chris@42 50 #define LEQ(x, y) (((x) & (y)) == (x))
Chris@42 51
Chris@42 52 /* A subsumes B */
Chris@42 53 static int subsumes(const flags_t *a, unsigned slvndx_a, const flags_t *b)
Chris@42 54 {
Chris@42 55 if (slvndx_a != INFEASIBLE_SLVNDX) {
Chris@42 56 A(a->timelimit_impatience == 0);
Chris@42 57 return (LEQ(a->u, b->u) && LEQ(b->l, a->l));
Chris@42 58 } else {
Chris@42 59 return (LEQ(a->l, b->l)
Chris@42 60 && a->timelimit_impatience <= b->timelimit_impatience);
Chris@42 61 }
Chris@42 62 }
Chris@42 63
Chris@42 64 static unsigned addmod(unsigned a, unsigned b, unsigned p)
Chris@42 65 {
Chris@42 66 /* gcc-2.95/sparc produces incorrect code for the fast version below. */
Chris@42 67 #if defined(__sparc__) && defined(__GNUC__)
Chris@42 68 /* slow version */
Chris@42 69 return (a + b) % p;
Chris@42 70 #else
Chris@42 71 /* faster version */
Chris@42 72 unsigned c = a + b;
Chris@42 73 return c >= p ? c - p : c;
Chris@42 74 #endif
Chris@42 75 }
Chris@42 76
Chris@42 77 /*
Chris@42 78 slvdesc management:
Chris@42 79 */
Chris@42 80 static void sgrow(planner *ego)
Chris@42 81 {
Chris@42 82 unsigned osiz = ego->slvdescsiz, nsiz = 1 + osiz + osiz / 4;
Chris@42 83 slvdesc *ntab = (slvdesc *)MALLOC(nsiz * sizeof(slvdesc), SLVDESCS);
Chris@42 84 slvdesc *otab = ego->slvdescs;
Chris@42 85 unsigned i;
Chris@42 86
Chris@42 87 ego->slvdescs = ntab;
Chris@42 88 ego->slvdescsiz = nsiz;
Chris@42 89 for (i = 0; i < osiz; ++i)
Chris@42 90 ntab[i] = otab[i];
Chris@42 91 X(ifree0)(otab);
Chris@42 92 }
Chris@42 93
Chris@42 94 static void register_solver(planner *ego, solver *s)
Chris@42 95 {
Chris@42 96 slvdesc *n;
Chris@42 97 int kind;
Chris@42 98
Chris@42 99 if (s) { /* add s to solver list */
Chris@42 100 X(solver_use)(s);
Chris@42 101
Chris@42 102 A(ego->nslvdesc < INFEASIBLE_SLVNDX);
Chris@42 103 if (ego->nslvdesc >= ego->slvdescsiz)
Chris@42 104 sgrow(ego);
Chris@42 105
Chris@42 106 n = ego->slvdescs + ego->nslvdesc;
Chris@42 107
Chris@42 108 n->slv = s;
Chris@42 109 n->reg_nam = ego->cur_reg_nam;
Chris@42 110 n->reg_id = ego->cur_reg_id++;
Chris@42 111
Chris@42 112 A(strlen(n->reg_nam) < MAXNAM);
Chris@42 113 n->nam_hash = X(hash)(n->reg_nam);
Chris@42 114
Chris@42 115 kind = s->adt->problem_kind;
Chris@42 116 n->next_for_same_problem_kind = ego->slvdescs_for_problem_kind[kind];
Chris@42 117 ego->slvdescs_for_problem_kind[kind] = (int)/*from unsigned*/ego->nslvdesc;
Chris@42 118
Chris@42 119 ego->nslvdesc++;
Chris@42 120 }
Chris@42 121 }
Chris@42 122
Chris@42 123 static unsigned slookup(planner *ego, char *nam, int id)
Chris@42 124 {
Chris@42 125 unsigned h = X(hash)(nam); /* used to avoid strcmp in the common case */
Chris@42 126 FORALL_SOLVERS(ego, s, sp, {
Chris@42 127 UNUSED(s);
Chris@42 128 if (sp->reg_id == id && sp->nam_hash == h
Chris@42 129 && !strcmp(sp->reg_nam, nam))
Chris@42 130 return (unsigned)/*from ptrdiff_t*/(sp - ego->slvdescs);
Chris@42 131 });
Chris@42 132 return INFEASIBLE_SLVNDX;
Chris@42 133 }
Chris@42 134
Chris@42 135 /* Compute a MD5 hash of the configuration of the planner.
Chris@42 136 We store it into the wisdom file to make absolutely sure that
Chris@42 137 we are reading wisdom that is applicable */
Chris@42 138 static void signature_of_configuration(md5 *m, planner *ego)
Chris@42 139 {
Chris@42 140 X(md5begin)(m);
Chris@42 141 X(md5unsigned)(m, sizeof(R)); /* so we don't mix different precisions */
Chris@42 142 FORALL_SOLVERS(ego, s, sp, {
Chris@42 143 UNUSED(s);
Chris@42 144 X(md5int)(m, sp->reg_id);
Chris@42 145 X(md5puts)(m, sp->reg_nam);
Chris@42 146 });
Chris@42 147 X(md5end)(m);
Chris@42 148 }
Chris@42 149
Chris@42 150 /*
Chris@42 151 md5-related stuff:
Chris@42 152 */
Chris@42 153
Chris@42 154 /* first hash function */
Chris@42 155 static unsigned h1(const hashtab *ht, const md5sig s)
Chris@42 156 {
Chris@42 157 unsigned h = s[0] % ht->hashsiz;
Chris@42 158 A(h == (s[0] % ht->hashsiz));
Chris@42 159 return h;
Chris@42 160 }
Chris@42 161
Chris@42 162 /* second hash function (for double hashing) */
Chris@42 163 static unsigned h2(const hashtab *ht, const md5sig s)
Chris@42 164 {
Chris@42 165 unsigned h = 1U + s[1] % (ht->hashsiz - 1);
Chris@42 166 A(h == (1U + s[1] % (ht->hashsiz - 1)));
Chris@42 167 return h;
Chris@42 168 }
Chris@42 169
Chris@42 170 static void md5hash(md5 *m, const problem *p, const planner *plnr)
Chris@42 171 {
Chris@42 172 X(md5begin)(m);
Chris@42 173 X(md5unsigned)(m, sizeof(R)); /* so we don't mix different precisions */
Chris@42 174 X(md5int)(m, plnr->nthr);
Chris@42 175 p->adt->hash(p, m);
Chris@42 176 X(md5end)(m);
Chris@42 177 }
Chris@42 178
Chris@42 179 static int md5eq(const md5sig a, const md5sig b)
Chris@42 180 {
Chris@42 181 return a[0] == b[0] && a[1] == b[1] && a[2] == b[2] && a[3] == b[3];
Chris@42 182 }
Chris@42 183
Chris@42 184 static void sigcpy(const md5sig a, md5sig b)
Chris@42 185 {
Chris@42 186 b[0] = a[0]; b[1] = a[1]; b[2] = a[2]; b[3] = a[3];
Chris@42 187 }
Chris@42 188
Chris@42 189 /*
Chris@42 190 memoization routines :
Chris@42 191 */
Chris@42 192
Chris@42 193 /*
Chris@42 194 liber scriptus proferetur
Chris@42 195 in quo totum continetur
Chris@42 196 unde mundus iudicetur
Chris@42 197 */
Chris@42 198 struct solution_s {
Chris@42 199 md5sig s;
Chris@42 200 flags_t flags;
Chris@42 201 };
Chris@42 202
Chris@42 203 static solution *htab_lookup(hashtab *ht, const md5sig s,
Chris@42 204 const flags_t *flagsp)
Chris@42 205 {
Chris@42 206 unsigned g, h = h1(ht, s), d = h2(ht, s);
Chris@42 207 solution *best = 0;
Chris@42 208
Chris@42 209 ++ht->lookup;
Chris@42 210
Chris@42 211 /* search all entries that match; select the one with
Chris@42 212 the lowest flags.u */
Chris@42 213 /* This loop may potentially traverse the whole table, since at
Chris@42 214 least one element is guaranteed to be !LIVEP, but all elements
Chris@42 215 may be VALIDP. Hence, we stop after at the first invalid
Chris@42 216 element or after traversing the whole table. */
Chris@42 217 g = h;
Chris@42 218 do {
Chris@42 219 solution *l = ht->solutions + g;
Chris@42 220 ++ht->lookup_iter;
Chris@42 221 if (VALIDP(l)) {
Chris@42 222 if (LIVEP(l)
Chris@42 223 && md5eq(s, l->s)
Chris@42 224 && subsumes(&l->flags, SLVNDX(l), flagsp) ) {
Chris@42 225 if (!best || LEQ(l->flags.u, best->flags.u))
Chris@42 226 best = l;
Chris@42 227 }
Chris@42 228 } else
Chris@42 229 break;
Chris@42 230
Chris@42 231 g = addmod(g, d, ht->hashsiz);
Chris@42 232 } while (g != h);
Chris@42 233
Chris@42 234 if (best)
Chris@42 235 ++ht->succ_lookup;
Chris@42 236 return best;
Chris@42 237 }
Chris@42 238
Chris@42 239 static solution *hlookup(planner *ego, const md5sig s,
Chris@42 240 const flags_t *flagsp)
Chris@42 241 {
Chris@42 242 solution *sol = htab_lookup(&ego->htab_blessed, s, flagsp);
Chris@42 243 if (!sol) sol = htab_lookup(&ego->htab_unblessed, s, flagsp);
Chris@42 244 return sol;
Chris@42 245 }
Chris@42 246
Chris@42 247 static void fill_slot(hashtab *ht, const md5sig s, const flags_t *flagsp,
Chris@42 248 unsigned slvndx, solution *slot)
Chris@42 249 {
Chris@42 250 ++ht->insert;
Chris@42 251 ++ht->nelem;
Chris@42 252 A(!LIVEP(slot));
Chris@42 253 slot->flags.u = flagsp->u;
Chris@42 254 slot->flags.l = flagsp->l;
Chris@42 255 slot->flags.timelimit_impatience = flagsp->timelimit_impatience;
Chris@42 256 slot->flags.hash_info |= H_VALID | H_LIVE;
Chris@42 257 SLVNDX(slot) = slvndx;
Chris@42 258
Chris@42 259 /* keep this check enabled in case we add so many solvers
Chris@42 260 that the bitfield overflows */
Chris@42 261 CK(SLVNDX(slot) == slvndx);
Chris@42 262 sigcpy(s, slot->s);
Chris@42 263 }
Chris@42 264
Chris@42 265 static void kill_slot(hashtab *ht, solution *slot)
Chris@42 266 {
Chris@42 267 A(LIVEP(slot)); /* ==> */ A(VALIDP(slot));
Chris@42 268
Chris@42 269 --ht->nelem;
Chris@42 270 slot->flags.hash_info = H_VALID;
Chris@42 271 }
Chris@42 272
Chris@42 273 static void hinsert0(hashtab *ht, const md5sig s, const flags_t *flagsp,
Chris@42 274 unsigned slvndx)
Chris@42 275 {
Chris@42 276 solution *l;
Chris@42 277 unsigned g, h = h1(ht, s), d = h2(ht, s);
Chris@42 278
Chris@42 279 ++ht->insert_unknown;
Chris@42 280
Chris@42 281 /* search for nonfull slot */
Chris@42 282 for (g = h; ; g = addmod(g, d, ht->hashsiz)) {
Chris@42 283 ++ht->insert_iter;
Chris@42 284 l = ht->solutions + g;
Chris@42 285 if (!LIVEP(l)) break;
Chris@42 286 A((g + d) % ht->hashsiz != h);
Chris@42 287 }
Chris@42 288
Chris@42 289 fill_slot(ht, s, flagsp, slvndx, l);
Chris@42 290 }
Chris@42 291
Chris@42 292 static void rehash(hashtab *ht, unsigned nsiz)
Chris@42 293 {
Chris@42 294 unsigned osiz = ht->hashsiz, h;
Chris@42 295 solution *osol = ht->solutions, *nsol;
Chris@42 296
Chris@42 297 nsiz = (unsigned)X(next_prime)((INT)nsiz);
Chris@42 298 nsol = (solution *)MALLOC(nsiz * sizeof(solution), HASHT);
Chris@42 299 ++ht->nrehash;
Chris@42 300
Chris@42 301 /* init new table */
Chris@42 302 for (h = 0; h < nsiz; ++h)
Chris@42 303 nsol[h].flags.hash_info = 0;
Chris@42 304
Chris@42 305 /* install new table */
Chris@42 306 ht->hashsiz = nsiz;
Chris@42 307 ht->solutions = nsol;
Chris@42 308 ht->nelem = 0;
Chris@42 309
Chris@42 310 /* copy table */
Chris@42 311 for (h = 0; h < osiz; ++h) {
Chris@42 312 solution *l = osol + h;
Chris@42 313 if (LIVEP(l))
Chris@42 314 hinsert0(ht, l->s, &l->flags, SLVNDX(l));
Chris@42 315 }
Chris@42 316
Chris@42 317 X(ifree0)(osol);
Chris@42 318 }
Chris@42 319
Chris@42 320 static unsigned minsz(unsigned nelem)
Chris@42 321 {
Chris@42 322 return 1U + nelem + nelem / 8U;
Chris@42 323 }
Chris@42 324
Chris@42 325 static unsigned nextsz(unsigned nelem)
Chris@42 326 {
Chris@42 327 return minsz(minsz(nelem));
Chris@42 328 }
Chris@42 329
Chris@42 330 static void hgrow(hashtab *ht)
Chris@42 331 {
Chris@42 332 unsigned nelem = ht->nelem;
Chris@42 333 if (minsz(nelem) >= ht->hashsiz)
Chris@42 334 rehash(ht, nextsz(nelem));
Chris@42 335 }
Chris@42 336
Chris@42 337 #if 0
Chris@42 338 /* shrink the hash table, never used */
Chris@42 339 static void hshrink(hashtab *ht)
Chris@42 340 {
Chris@42 341 unsigned nelem = ht->nelem;
Chris@42 342 /* always rehash after deletions */
Chris@42 343 rehash(ht, nextsz(nelem));
Chris@42 344 }
Chris@42 345 #endif
Chris@42 346
Chris@42 347 static void htab_insert(hashtab *ht, const md5sig s, const flags_t *flagsp,
Chris@42 348 unsigned slvndx)
Chris@42 349 {
Chris@42 350 unsigned g, h = h1(ht, s), d = h2(ht, s);
Chris@42 351 solution *first = 0;
Chris@42 352
Chris@42 353 /* Remove all entries that are subsumed by the new one. */
Chris@42 354 /* This loop may potentially traverse the whole table, since at
Chris@42 355 least one element is guaranteed to be !LIVEP, but all elements
Chris@42 356 may be VALIDP. Hence, we stop after at the first invalid
Chris@42 357 element or after traversing the whole table. */
Chris@42 358 g = h;
Chris@42 359 do {
Chris@42 360 solution *l = ht->solutions + g;
Chris@42 361 ++ht->insert_iter;
Chris@42 362 if (VALIDP(l)) {
Chris@42 363 if (LIVEP(l) && md5eq(s, l->s)) {
Chris@42 364 if (subsumes(flagsp, slvndx, &l->flags)) {
Chris@42 365 if (!first) first = l;
Chris@42 366 kill_slot(ht, l);
Chris@42 367 } else {
Chris@42 368 /* It is an error to insert an element that
Chris@42 369 is subsumed by an existing entry. */
Chris@42 370 A(!subsumes(&l->flags, SLVNDX(l), flagsp));
Chris@42 371 }
Chris@42 372 }
Chris@42 373 } else
Chris@42 374 break;
Chris@42 375
Chris@42 376 g = addmod(g, d, ht->hashsiz);
Chris@42 377 } while (g != h);
Chris@42 378
Chris@42 379 if (first) {
Chris@42 380 /* overwrite FIRST */
Chris@42 381 fill_slot(ht, s, flagsp, slvndx, first);
Chris@42 382 } else {
Chris@42 383 /* create a new entry */
Chris@42 384 hgrow(ht);
Chris@42 385 hinsert0(ht, s, flagsp, slvndx);
Chris@42 386 }
Chris@42 387 }
Chris@42 388
Chris@42 389 static void hinsert(planner *ego, const md5sig s, const flags_t *flagsp,
Chris@42 390 unsigned slvndx)
Chris@42 391 {
Chris@42 392 htab_insert(BLISS(*flagsp) ? &ego->htab_blessed : &ego->htab_unblessed,
Chris@42 393 s, flagsp, slvndx );
Chris@42 394 }
Chris@42 395
Chris@42 396
Chris@42 397 static void invoke_hook(planner *ego, plan *pln, const problem *p,
Chris@42 398 int optimalp)
Chris@42 399 {
Chris@42 400 if (ego->hook)
Chris@42 401 ego->hook(ego, pln, p, optimalp);
Chris@42 402 }
Chris@42 403
Chris@42 404 #ifdef FFTW_RANDOM_ESTIMATOR
Chris@42 405 /* a "random" estimate, used for debugging to generate "random"
Chris@42 406 plans, albeit from a deterministic seed. */
Chris@42 407
Chris@42 408 unsigned X(random_estimate_seed) = 0;
Chris@42 409
Chris@42 410 static double random_estimate(const planner *ego, const plan *pln,
Chris@42 411 const problem *p)
Chris@42 412 {
Chris@42 413 md5 m;
Chris@42 414 X(md5begin)(&m);
Chris@42 415 X(md5unsigned)(&m, X(random_estimate_seed));
Chris@42 416 X(md5int)(&m, ego->nthr);
Chris@42 417 p->adt->hash(p, &m);
Chris@42 418 X(md5putb)(&m, &pln->ops, sizeof(pln->ops));
Chris@42 419 X(md5putb)(&m, &pln->adt, sizeof(pln->adt));
Chris@42 420 X(md5end)(&m);
Chris@42 421 return ego->cost_hook ? ego->cost_hook(p, m.s[0], COST_MAX) : m.s[0];
Chris@42 422 }
Chris@42 423
Chris@42 424 #endif
Chris@42 425
Chris@42 426 double X(iestimate_cost)(const planner *ego, const plan *pln, const problem *p)
Chris@42 427 {
Chris@42 428 double cost =
Chris@42 429 + pln->ops.add
Chris@42 430 + pln->ops.mul
Chris@42 431
Chris@42 432 #if HAVE_FMA
Chris@42 433 + pln->ops.fma
Chris@42 434 #else
Chris@42 435 + 2 * pln->ops.fma
Chris@42 436 #endif
Chris@42 437
Chris@42 438 + pln->ops.other;
Chris@42 439 if (ego->cost_hook)
Chris@42 440 cost = ego->cost_hook(p, cost, COST_MAX);
Chris@42 441 return cost;
Chris@42 442 }
Chris@42 443
Chris@42 444 static void evaluate_plan(planner *ego, plan *pln, const problem *p)
Chris@42 445 {
Chris@42 446 if (ESTIMATEP(ego) || !BELIEVE_PCOSTP(ego) || pln->pcost == 0.0) {
Chris@42 447 ego->nplan++;
Chris@42 448
Chris@42 449 if (ESTIMATEP(ego)) {
Chris@42 450 estimate:
Chris@42 451 /* heuristic */
Chris@42 452 #ifdef FFTW_RANDOM_ESTIMATOR
Chris@42 453 pln->pcost = random_estimate(ego, pln, p);
Chris@42 454 ego->epcost += X(iestimate_cost)(ego, pln, p);
Chris@42 455 #else
Chris@42 456 pln->pcost = X(iestimate_cost)(ego, pln, p);
Chris@42 457 ego->epcost += pln->pcost;
Chris@42 458 #endif
Chris@42 459 } else {
Chris@42 460 double t = X(measure_execution_time)(ego, pln, p);
Chris@42 461
Chris@42 462 if (t < 0) { /* unavailable cycle counter */
Chris@42 463 /* Real programmers can write FORTRAN in any language */
Chris@42 464 goto estimate;
Chris@42 465 }
Chris@42 466
Chris@42 467 pln->pcost = t;
Chris@42 468 ego->pcost += t;
Chris@42 469 ego->need_timeout_check = 1;
Chris@42 470 }
Chris@42 471 }
Chris@42 472
Chris@42 473 invoke_hook(ego, pln, p, 0);
Chris@42 474 }
Chris@42 475
Chris@42 476 /* maintain dynamic scoping of flags, nthr: */
Chris@42 477 static plan *invoke_solver(planner *ego, const problem *p, solver *s,
Chris@42 478 const flags_t *nflags)
Chris@42 479 {
Chris@42 480 flags_t flags = ego->flags;
Chris@42 481 int nthr = ego->nthr;
Chris@42 482 plan *pln;
Chris@42 483 ego->flags = *nflags;
Chris@42 484 PLNR_TIMELIMIT_IMPATIENCE(ego) = 0;
Chris@42 485 A(p->adt->problem_kind == s->adt->problem_kind);
Chris@42 486 pln = s->adt->mkplan(s, p, ego);
Chris@42 487 ego->nthr = nthr;
Chris@42 488 ego->flags = flags;
Chris@42 489 return pln;
Chris@42 490 }
Chris@42 491
Chris@42 492 /* maintain the invariant TIMED_OUT ==> NEED_TIMEOUT_CHECK */
Chris@42 493 static int timeout_p(planner *ego, const problem *p)
Chris@42 494 {
Chris@42 495 /* do not timeout when estimating. First, the estimator is the
Chris@42 496 planner of last resort. Second, calling X(elapsed_since)() is
Chris@42 497 slower than estimating */
Chris@42 498 if (!ESTIMATEP(ego)) {
Chris@42 499 /* do not assume that X(elapsed_since)() is monotonic */
Chris@42 500 if (ego->timed_out) {
Chris@42 501 A(ego->need_timeout_check);
Chris@42 502 return 1;
Chris@42 503 }
Chris@42 504
Chris@42 505 if (ego->timelimit >= 0 &&
Chris@42 506 X(elapsed_since)(ego, p, ego->start_time) >= ego->timelimit) {
Chris@42 507 ego->timed_out = 1;
Chris@42 508 ego->need_timeout_check = 1;
Chris@42 509 return 1;
Chris@42 510 }
Chris@42 511 }
Chris@42 512
Chris@42 513 A(!ego->timed_out);
Chris@42 514 ego->need_timeout_check = 0;
Chris@42 515 return 0;
Chris@42 516 }
Chris@42 517
Chris@42 518 static plan *search0(planner *ego, const problem *p, unsigned *slvndx,
Chris@42 519 const flags_t *flagsp)
Chris@42 520 {
Chris@42 521 plan *best = 0;
Chris@42 522 int best_not_yet_timed = 1;
Chris@42 523
Chris@42 524 /* Do not start a search if the planner timed out. This check is
Chris@42 525 necessary, lest the relaxation mechanism kick in */
Chris@42 526 if (timeout_p(ego, p))
Chris@42 527 return 0;
Chris@42 528
Chris@42 529 FORALL_SOLVERS_OF_KIND(p->adt->problem_kind, ego, s, sp, {
Chris@42 530 plan *pln;
Chris@42 531
Chris@42 532 pln = invoke_solver(ego, p, s, flagsp);
Chris@42 533
Chris@42 534 if (ego->need_timeout_check)
Chris@42 535 if (timeout_p(ego, p)) {
Chris@42 536 X(plan_destroy_internal)(pln);
Chris@42 537 X(plan_destroy_internal)(best);
Chris@42 538 return 0;
Chris@42 539 }
Chris@42 540
Chris@42 541 if (pln) {
Chris@42 542 /* read COULD_PRUNE_NOW_P because PLN may be destroyed
Chris@42 543 before we use COULD_PRUNE_NOW_P */
Chris@42 544 int could_prune_now_p = pln->could_prune_now_p;
Chris@42 545
Chris@42 546 if (best) {
Chris@42 547 if (best_not_yet_timed) {
Chris@42 548 evaluate_plan(ego, best, p);
Chris@42 549 best_not_yet_timed = 0;
Chris@42 550 }
Chris@42 551 evaluate_plan(ego, pln, p);
Chris@42 552 if (pln->pcost < best->pcost) {
Chris@42 553 X(plan_destroy_internal)(best);
Chris@42 554 best = pln;
Chris@42 555 *slvndx = (unsigned)/*from ptrdiff_t*/(sp - ego->slvdescs);
Chris@42 556 } else {
Chris@42 557 X(plan_destroy_internal)(pln);
Chris@42 558 }
Chris@42 559 } else {
Chris@42 560 best = pln;
Chris@42 561 *slvndx = (unsigned)/*from ptrdiff_t*/(sp - ego->slvdescs);
Chris@42 562 }
Chris@42 563
Chris@42 564 if (ALLOW_PRUNINGP(ego) && could_prune_now_p)
Chris@42 565 break;
Chris@42 566 }
Chris@42 567 });
Chris@42 568
Chris@42 569 return best;
Chris@42 570 }
Chris@42 571
Chris@42 572 static plan *search(planner *ego, const problem *p, unsigned *slvndx,
Chris@42 573 flags_t *flagsp)
Chris@42 574 {
Chris@42 575 plan *pln = 0;
Chris@42 576 unsigned i;
Chris@42 577
Chris@42 578 /* relax impatience in this order: */
Chris@42 579 static const unsigned relax_tab[] = {
Chris@42 580 0, /* relax nothing */
Chris@42 581 NO_VRECURSE,
Chris@42 582 NO_FIXED_RADIX_LARGE_N,
Chris@42 583 NO_SLOW,
Chris@42 584 NO_UGLY
Chris@42 585 };
Chris@42 586
Chris@42 587 unsigned l_orig = flagsp->l;
Chris@42 588 unsigned x = flagsp->u;
Chris@42 589
Chris@42 590 /* guaranteed to be different from X */
Chris@42 591 unsigned last_x = ~x;
Chris@42 592
Chris@42 593 for (i = 0; i < sizeof(relax_tab) / sizeof(relax_tab[0]); ++i) {
Chris@42 594 if (LEQ(l_orig, x & ~relax_tab[i]))
Chris@42 595 x = x & ~relax_tab[i];
Chris@42 596
Chris@42 597 if (x != last_x) {
Chris@42 598 last_x = x;
Chris@42 599 flagsp->l = x;
Chris@42 600 pln = search0(ego, p, slvndx, flagsp);
Chris@42 601 if (pln) break;
Chris@42 602 }
Chris@42 603 }
Chris@42 604
Chris@42 605 if (!pln) {
Chris@42 606 /* search [L_ORIG, U] */
Chris@42 607 if (l_orig != last_x) {
Chris@42 608 last_x = l_orig;
Chris@42 609 flagsp->l = l_orig;
Chris@42 610 pln = search0(ego, p, slvndx, flagsp);
Chris@42 611 }
Chris@42 612 }
Chris@42 613
Chris@42 614 return pln;
Chris@42 615 }
Chris@42 616
Chris@42 617 #define CHECK_FOR_BOGOSITY \
Chris@42 618 if ((ego->bogosity_hook ? \
Chris@42 619 (ego->wisdom_state = ego->bogosity_hook(ego->wisdom_state, p)) \
Chris@42 620 : ego->wisdom_state) == WISDOM_IS_BOGUS) \
Chris@42 621 goto wisdom_is_bogus;
Chris@42 622
Chris@42 623 static plan *mkplan(planner *ego, const problem *p)
Chris@42 624 {
Chris@42 625 plan *pln;
Chris@42 626 md5 m;
Chris@42 627 unsigned slvndx;
Chris@42 628 flags_t flags_of_solution;
Chris@42 629 solution *sol;
Chris@42 630 solver *s;
Chris@42 631
Chris@42 632 ASSERT_ALIGNED_DOUBLE;
Chris@42 633 A(LEQ(PLNR_L(ego), PLNR_U(ego)));
Chris@42 634
Chris@42 635 if (ESTIMATEP(ego))
Chris@42 636 PLNR_TIMELIMIT_IMPATIENCE(ego) = 0; /* canonical form */
Chris@42 637
Chris@42 638
Chris@42 639 #ifdef FFTW_DEBUG
Chris@42 640 check(&ego->htab_blessed);
Chris@42 641 check(&ego->htab_unblessed);
Chris@42 642 #endif
Chris@42 643
Chris@42 644 pln = 0;
Chris@42 645
Chris@42 646 CHECK_FOR_BOGOSITY;
Chris@42 647
Chris@42 648 ego->timed_out = 0;
Chris@42 649
Chris@42 650 ++ego->nprob;
Chris@42 651 md5hash(&m, p, ego);
Chris@42 652
Chris@42 653 flags_of_solution = ego->flags;
Chris@42 654
Chris@42 655 if (ego->wisdom_state != WISDOM_IGNORE_ALL) {
Chris@42 656 if ((sol = hlookup(ego, m.s, &flags_of_solution))) {
Chris@42 657 /* wisdom is acceptable */
Chris@42 658 wisdom_state_t owisdom_state = ego->wisdom_state;
Chris@42 659
Chris@42 660 /* this hook is mainly for MPI, to make sure that
Chris@42 661 wisdom is in sync across all processes for MPI problems */
Chris@42 662 if (ego->wisdom_ok_hook && !ego->wisdom_ok_hook(p, sol->flags))
Chris@42 663 goto do_search; /* ignore not-ok wisdom */
Chris@42 664
Chris@42 665 slvndx = SLVNDX(sol);
Chris@42 666
Chris@42 667 if (slvndx == INFEASIBLE_SLVNDX) {
Chris@42 668 if (ego->wisdom_state == WISDOM_IGNORE_INFEASIBLE)
Chris@42 669 goto do_search;
Chris@42 670 else
Chris@42 671 return 0; /* known to be infeasible */
Chris@42 672 }
Chris@42 673
Chris@42 674 flags_of_solution = sol->flags;
Chris@42 675
Chris@42 676 /* inherit blessing either from wisdom
Chris@42 677 or from the planner */
Chris@42 678 flags_of_solution.hash_info |= BLISS(ego->flags);
Chris@42 679
Chris@42 680 ego->wisdom_state = WISDOM_ONLY;
Chris@42 681
Chris@42 682 s = ego->slvdescs[slvndx].slv;
Chris@42 683 if (p->adt->problem_kind != s->adt->problem_kind)
Chris@42 684 goto wisdom_is_bogus;
Chris@42 685
Chris@42 686 pln = invoke_solver(ego, p, s, &flags_of_solution);
Chris@42 687
Chris@42 688 CHECK_FOR_BOGOSITY; /* catch error in child solvers */
Chris@42 689
Chris@42 690 sol = 0; /* Paranoia: SOL may be dangling after
Chris@42 691 invoke_solver(); make sure we don't accidentally
Chris@42 692 reuse it. */
Chris@42 693
Chris@42 694 if (!pln)
Chris@42 695 goto wisdom_is_bogus;
Chris@42 696
Chris@42 697 ego->wisdom_state = owisdom_state;
Chris@42 698
Chris@42 699 goto skip_search;
Chris@42 700 }
Chris@42 701 else if (ego->nowisdom_hook) /* for MPI, make sure lack of wisdom */
Chris@42 702 ego->nowisdom_hook(p); /* is in sync across all processes */
Chris@42 703 }
Chris@42 704
Chris@42 705 do_search:
Chris@42 706 /* cannot search in WISDOM_ONLY mode */
Chris@42 707 if (ego->wisdom_state == WISDOM_ONLY)
Chris@42 708 goto wisdom_is_bogus;
Chris@42 709
Chris@42 710 flags_of_solution = ego->flags;
Chris@42 711 pln = search(ego, p, &slvndx, &flags_of_solution);
Chris@42 712 CHECK_FOR_BOGOSITY; /* catch error in child solvers */
Chris@42 713
Chris@42 714 if (ego->timed_out) {
Chris@42 715 A(!pln);
Chris@42 716 if (PLNR_TIMELIMIT_IMPATIENCE(ego) != 0) {
Chris@42 717 /* record (below) that this plan has failed because of
Chris@42 718 timeout */
Chris@42 719 flags_of_solution.hash_info |= BLESSING;
Chris@42 720 } else {
Chris@42 721 /* this is not the top-level problem or timeout is not
Chris@42 722 active: record no wisdom. */
Chris@42 723 return 0;
Chris@42 724 }
Chris@42 725 } else {
Chris@42 726 /* canonicalize to infinite timeout */
Chris@42 727 flags_of_solution.timelimit_impatience = 0;
Chris@42 728 }
Chris@42 729
Chris@42 730 skip_search:
Chris@42 731 if (ego->wisdom_state == WISDOM_NORMAL ||
Chris@42 732 ego->wisdom_state == WISDOM_ONLY) {
Chris@42 733 if (pln) {
Chris@42 734 hinsert(ego, m.s, &flags_of_solution, slvndx);
Chris@42 735 invoke_hook(ego, pln, p, 1);
Chris@42 736 } else {
Chris@42 737 hinsert(ego, m.s, &flags_of_solution, INFEASIBLE_SLVNDX);
Chris@42 738 }
Chris@42 739 }
Chris@42 740
Chris@42 741 return pln;
Chris@42 742
Chris@42 743 wisdom_is_bogus:
Chris@42 744 X(plan_destroy_internal)(pln);
Chris@42 745 ego->wisdom_state = WISDOM_IS_BOGUS;
Chris@42 746 return 0;
Chris@42 747 }
Chris@42 748
Chris@42 749 static void htab_destroy(hashtab *ht)
Chris@42 750 {
Chris@42 751 X(ifree)(ht->solutions);
Chris@42 752 ht->solutions = 0;
Chris@42 753 ht->nelem = 0U;
Chris@42 754 }
Chris@42 755
Chris@42 756 static void mkhashtab(hashtab *ht)
Chris@42 757 {
Chris@42 758 ht->nrehash = 0;
Chris@42 759 ht->succ_lookup = ht->lookup = ht->lookup_iter = 0;
Chris@42 760 ht->insert = ht->insert_iter = ht->insert_unknown = 0;
Chris@42 761
Chris@42 762 ht->solutions = 0;
Chris@42 763 ht->hashsiz = ht->nelem = 0U;
Chris@42 764 hgrow(ht); /* so that hashsiz > 0 */
Chris@42 765 }
Chris@42 766
Chris@42 767 /* destroy hash table entries. If FORGET_EVERYTHING, destroy the whole
Chris@42 768 table. If FORGET_ACCURSED, then destroy entries that are not blessed. */
Chris@42 769 static void forget(planner *ego, amnesia a)
Chris@42 770 {
Chris@42 771 switch (a) {
Chris@42 772 case FORGET_EVERYTHING:
Chris@42 773 htab_destroy(&ego->htab_blessed);
Chris@42 774 mkhashtab(&ego->htab_blessed);
Chris@42 775 /* fall through */
Chris@42 776 case FORGET_ACCURSED:
Chris@42 777 htab_destroy(&ego->htab_unblessed);
Chris@42 778 mkhashtab(&ego->htab_unblessed);
Chris@42 779 break;
Chris@42 780 default:
Chris@42 781 break;
Chris@42 782 }
Chris@42 783 }
Chris@42 784
Chris@42 785 /* FIXME: what sort of version information should we write? */
Chris@42 786 #define WISDOM_PREAMBLE PACKAGE "-" VERSION " " STRINGIZE(X(wisdom))
Chris@42 787 static const char stimeout[] = "TIMEOUT";
Chris@42 788
Chris@42 789 /* tantus labor non sit cassus */
Chris@42 790 static void exprt(planner *ego, printer *p)
Chris@42 791 {
Chris@42 792 unsigned h;
Chris@42 793 hashtab *ht = &ego->htab_blessed;
Chris@42 794 md5 m;
Chris@42 795
Chris@42 796 signature_of_configuration(&m, ego);
Chris@42 797
Chris@42 798 p->print(p,
Chris@42 799 "(" WISDOM_PREAMBLE " #x%M #x%M #x%M #x%M\n",
Chris@42 800 m.s[0], m.s[1], m.s[2], m.s[3]);
Chris@42 801
Chris@42 802 for (h = 0; h < ht->hashsiz; ++h) {
Chris@42 803 solution *l = ht->solutions + h;
Chris@42 804 if (LIVEP(l)) {
Chris@42 805 const char *reg_nam;
Chris@42 806 int reg_id;
Chris@42 807
Chris@42 808 if (SLVNDX(l) == INFEASIBLE_SLVNDX) {
Chris@42 809 reg_nam = stimeout;
Chris@42 810 reg_id = 0;
Chris@42 811 } else {
Chris@42 812 slvdesc *sp = ego->slvdescs + SLVNDX(l);
Chris@42 813 reg_nam = sp->reg_nam;
Chris@42 814 reg_id = sp->reg_id;
Chris@42 815 }
Chris@42 816
Chris@42 817 /* qui salvandos salvas gratis
Chris@42 818 salva me fons pietatis */
Chris@42 819 p->print(p, " (%s %d #x%x #x%x #x%x #x%M #x%M #x%M #x%M)\n",
Chris@42 820 reg_nam, reg_id,
Chris@42 821 l->flags.l, l->flags.u, l->flags.timelimit_impatience,
Chris@42 822 l->s[0], l->s[1], l->s[2], l->s[3]);
Chris@42 823 }
Chris@42 824 }
Chris@42 825 p->print(p, ")\n");
Chris@42 826 }
Chris@42 827
Chris@42 828 /* mors stupebit et natura
Chris@42 829 cum resurget creatura */
Chris@42 830 static int imprt(planner *ego, scanner *sc)
Chris@42 831 {
Chris@42 832 char buf[MAXNAM + 1];
Chris@42 833 md5uint sig[4];
Chris@42 834 unsigned l, u, timelimit_impatience;
Chris@42 835 flags_t flags;
Chris@42 836 int reg_id;
Chris@42 837 unsigned slvndx;
Chris@42 838 hashtab *ht = &ego->htab_blessed;
Chris@42 839 hashtab old;
Chris@42 840 md5 m;
Chris@42 841
Chris@42 842 if (!sc->scan(sc,
Chris@42 843 "(" WISDOM_PREAMBLE " #x%M #x%M #x%M #x%M\n",
Chris@42 844 sig + 0, sig + 1, sig + 2, sig + 3))
Chris@42 845 return 0; /* don't need to restore hashtable */
Chris@42 846
Chris@42 847 signature_of_configuration(&m, ego);
Chris@42 848 if (m.s[0] != sig[0] || m.s[1] != sig[1] ||
Chris@42 849 m.s[2] != sig[2] || m.s[3] != sig[3]) {
Chris@42 850 /* invalid configuration */
Chris@42 851 return 0;
Chris@42 852 }
Chris@42 853
Chris@42 854 /* make a backup copy of the hash table (cache the hash) */
Chris@42 855 {
Chris@42 856 unsigned h, hsiz = ht->hashsiz;
Chris@42 857 old = *ht;
Chris@42 858 old.solutions = (solution *)MALLOC(hsiz * sizeof(solution), HASHT);
Chris@42 859 for (h = 0; h < hsiz; ++h)
Chris@42 860 old.solutions[h] = ht->solutions[h];
Chris@42 861 }
Chris@42 862
Chris@42 863 while (1) {
Chris@42 864 if (sc->scan(sc, ")"))
Chris@42 865 break;
Chris@42 866
Chris@42 867 /* qua resurget ex favilla */
Chris@42 868 if (!sc->scan(sc, "(%*s %d #x%x #x%x #x%x #x%M #x%M #x%M #x%M)",
Chris@42 869 MAXNAM, buf, &reg_id, &l, &u, &timelimit_impatience,
Chris@42 870 sig + 0, sig + 1, sig + 2, sig + 3))
Chris@42 871 goto bad;
Chris@42 872
Chris@42 873 if (!strcmp(buf, stimeout) && reg_id == 0) {
Chris@42 874 slvndx = INFEASIBLE_SLVNDX;
Chris@42 875 } else {
Chris@42 876 if (timelimit_impatience != 0)
Chris@42 877 goto bad;
Chris@42 878
Chris@42 879 slvndx = slookup(ego, buf, reg_id);
Chris@42 880 if (slvndx == INFEASIBLE_SLVNDX)
Chris@42 881 goto bad;
Chris@42 882 }
Chris@42 883
Chris@42 884 /* inter oves locum praesta */
Chris@42 885 flags.l = l;
Chris@42 886 flags.u = u;
Chris@42 887 flags.timelimit_impatience = timelimit_impatience;
Chris@42 888 flags.hash_info = BLESSING;
Chris@42 889
Chris@42 890 CK(flags.l == l);
Chris@42 891 CK(flags.u == u);
Chris@42 892 CK(flags.timelimit_impatience == timelimit_impatience);
Chris@42 893
Chris@42 894 if (!hlookup(ego, sig, &flags))
Chris@42 895 hinsert(ego, sig, &flags, slvndx);
Chris@42 896 }
Chris@42 897
Chris@42 898 X(ifree0)(old.solutions);
Chris@42 899 return 1;
Chris@42 900
Chris@42 901 bad:
Chris@42 902 /* ``The wisdom of FFTW must be above suspicion.'' */
Chris@42 903 X(ifree0)(ht->solutions);
Chris@42 904 *ht = old;
Chris@42 905 return 0;
Chris@42 906 }
Chris@42 907
Chris@42 908 /*
Chris@42 909 * create a planner
Chris@42 910 */
Chris@42 911 planner *X(mkplanner)(void)
Chris@42 912 {
Chris@42 913 int i;
Chris@42 914
Chris@42 915 static const planner_adt padt = {
Chris@42 916 register_solver, mkplan, forget, exprt, imprt
Chris@42 917 };
Chris@42 918
Chris@42 919 planner *p = (planner *) MALLOC(sizeof(planner), PLANNERS);
Chris@42 920
Chris@42 921 p->adt = &padt;
Chris@42 922 p->nplan = p->nprob = 0;
Chris@42 923 p->pcost = p->epcost = 0.0;
Chris@42 924 p->hook = 0;
Chris@42 925 p->cost_hook = 0;
Chris@42 926 p->wisdom_ok_hook = 0;
Chris@42 927 p->nowisdom_hook = 0;
Chris@42 928 p->bogosity_hook = 0;
Chris@42 929 p->cur_reg_nam = 0;
Chris@42 930 p->wisdom_state = WISDOM_NORMAL;
Chris@42 931
Chris@42 932 p->slvdescs = 0;
Chris@42 933 p->nslvdesc = p->slvdescsiz = 0;
Chris@42 934
Chris@42 935 p->flags.l = 0;
Chris@42 936 p->flags.u = 0;
Chris@42 937 p->flags.timelimit_impatience = 0;
Chris@42 938 p->flags.hash_info = 0;
Chris@42 939 p->nthr = 1;
Chris@42 940 p->need_timeout_check = 1;
Chris@42 941 p->timelimit = -1;
Chris@42 942
Chris@42 943 mkhashtab(&p->htab_blessed);
Chris@42 944 mkhashtab(&p->htab_unblessed);
Chris@42 945
Chris@42 946 for (i = 0; i < PROBLEM_LAST; ++i)
Chris@42 947 p->slvdescs_for_problem_kind[i] = -1;
Chris@42 948
Chris@42 949 return p;
Chris@42 950 }
Chris@42 951
Chris@42 952 void X(planner_destroy)(planner *ego)
Chris@42 953 {
Chris@42 954 /* destroy hash table */
Chris@42 955 htab_destroy(&ego->htab_blessed);
Chris@42 956 htab_destroy(&ego->htab_unblessed);
Chris@42 957
Chris@42 958 /* destroy solvdesc table */
Chris@42 959 FORALL_SOLVERS(ego, s, sp, {
Chris@42 960 UNUSED(sp);
Chris@42 961 X(solver_destroy)(s);
Chris@42 962 });
Chris@42 963
Chris@42 964 X(ifree0)(ego->slvdescs);
Chris@42 965 X(ifree)(ego); /* dona eis requiem */
Chris@42 966 }
Chris@42 967
Chris@42 968 plan *X(mkplan_d)(planner *ego, problem *p)
Chris@42 969 {
Chris@42 970 plan *pln = ego->adt->mkplan(ego, p);
Chris@42 971 X(problem_destroy)(p);
Chris@42 972 return pln;
Chris@42 973 }
Chris@42 974
Chris@42 975 /* like X(mkplan_d), but sets/resets flags as well */
Chris@42 976 plan *X(mkplan_f_d)(planner *ego, problem *p,
Chris@42 977 unsigned l_set, unsigned u_set, unsigned u_reset)
Chris@42 978 {
Chris@42 979 flags_t oflags = ego->flags;
Chris@42 980 plan *pln;
Chris@42 981
Chris@42 982 PLNR_U(ego) &= ~u_reset;
Chris@42 983 PLNR_L(ego) &= ~u_reset;
Chris@42 984 PLNR_L(ego) |= l_set;
Chris@42 985 PLNR_U(ego) |= u_set | l_set;
Chris@42 986 pln = X(mkplan_d)(ego, p);
Chris@42 987 ego->flags = oflags;
Chris@42 988 return pln;
Chris@42 989 }
Chris@42 990
Chris@42 991 /*
Chris@42 992 * Debugging code:
Chris@42 993 */
Chris@42 994 #ifdef FFTW_DEBUG
Chris@42 995 static void check(hashtab *ht)
Chris@42 996 {
Chris@42 997 unsigned live = 0;
Chris@42 998 unsigned i;
Chris@42 999
Chris@42 1000 A(ht->nelem < ht->hashsiz);
Chris@42 1001
Chris@42 1002 for (i = 0; i < ht->hashsiz; ++i) {
Chris@42 1003 solution *l = ht->solutions + i;
Chris@42 1004 if (LIVEP(l))
Chris@42 1005 ++live;
Chris@42 1006 }
Chris@42 1007
Chris@42 1008 A(ht->nelem == live);
Chris@42 1009
Chris@42 1010 for (i = 0; i < ht->hashsiz; ++i) {
Chris@42 1011 solution *l1 = ht->solutions + i;
Chris@42 1012 int foundit = 0;
Chris@42 1013 if (LIVEP(l1)) {
Chris@42 1014 unsigned g, h = h1(ht, l1->s), d = h2(ht, l1->s);
Chris@42 1015
Chris@42 1016 g = h;
Chris@42 1017 do {
Chris@42 1018 solution *l = ht->solutions + g;
Chris@42 1019 if (VALIDP(l)) {
Chris@42 1020 if (l1 == l)
Chris@42 1021 foundit = 1;
Chris@42 1022 else if (LIVEP(l) && md5eq(l1->s, l->s)) {
Chris@42 1023 A(!subsumes(&l->flags, SLVNDX(l), &l1->flags));
Chris@42 1024 A(!subsumes(&l1->flags, SLVNDX(l1), &l->flags));
Chris@42 1025 }
Chris@42 1026 } else
Chris@42 1027 break;
Chris@42 1028 g = addmod(g, d, ht->hashsiz);
Chris@42 1029 } while (g != h);
Chris@42 1030
Chris@42 1031 A(foundit);
Chris@42 1032 }
Chris@42 1033 }
Chris@42 1034 }
Chris@42 1035 #endif