annotate src/fftw-3.3.5/kernel/planner.c @ 167:bd3cc4d1df30

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