#ifndef NORCSID static char rcsid[] = "$Id$"; #endif #include #include #include #include #include "param.h" #include "tables.h" #include "types.h" #include #include "data.h" #include "result.h" #include "state.h" #include "equiv.h" #include "extern.h" #ifdef REGVARS #include "regvar.h" /* regreturn */ #endif /* * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. * See the copyright notice in the ACK home directory, in the file "Copyright". * * Author: Hans van Staveren */ #define ALLOW_NEXTEM /* code generator is allowed new try of NEXTEM \ in exceptional cases */ byte startupcode[] = { DO_NEXTEM }; #ifdef NDEBUG #define DEBUG(string) #else #include #define DEBUG(string) \ { \ if (Debug) \ fprintf(stderr, "%-*d%s\n", 4 * level, level, string); \ } #endif #define BROKE() \ { \ assert(origcp != startupcode || !paniced); \ DEBUG("BROKE"); \ totalcost = INFINITY; \ goto doreturn; \ } #define CHKCOST() \ { \ if (totalcost >= costlimit) \ BROKE(); \ } #ifdef TABLEDEBUG int tablelines[MAXTDBUG]; int ntableline; int set_fd, set_size; short* set_val; char* set_flag; #endif unsigned codegen(byte* codep, int ply, int toplevel, unsigned costlimit, int forced) { #ifndef NDEBUG byte* origcp = codep; static int level = 0; #endif unsigned totalcost = 0; int inscoerc = 0; int procarg[MAXPROCARG + 1] = {}; #ifdef ALLOW_NEXTEM static int paniced; char* savebp = 0; #endif state_t state; #define SAVEST savestatus(&state) #define RESTST restorestatus(&state) #define FREEST /* nothing */ #ifdef TABLEDEBUG extern char* tablename; #endif #ifndef NDEBUG assert(costlimit <= INFINITY); level++; DEBUG("Entering codegen"); if (Debug > 1) fprintf(stderr, "toplevel = %d\n", toplevel); #endif for (;;) { switch ((*codep++) & 037) { default: assert(FALSE); /* NOTREACHED */ #ifdef TABLEDEBUG case DO_DLINE: { int n; getint(n, codep); tablelines[ntableline++] = n; if (ntableline >= MAXTDBUG) ntableline -= MAXTDBUG; if (set_fd) set_val[n >> 4] &= ~(1 << (n & 017)); #ifndef NDEBUG if (Debug) fprintf(stderr, "code from \"%s\", line %d\n", tablename, n); #endif break; } #endif case DO_NEXTEM: { byte* bp; int n; unsigned mindistance, dist; int i; int cindex; int npos, pos[MAXRULE]; unsigned mincost, t; DEBUG("NEXTEM"); tokpatlen = 0; nallreg = 0; if (toplevel) { garbage_collect(); totalcost = 0; } else { if (--ply <= 0) goto doreturn; } if (stackheight > MAXFSTACK - 7) { #ifndef NDEBUG if (Debug) fprintf(stderr, "Fakestack overflow threatens(%d), action ...\n", stackheight); #endif totalcost += stackupto(&fakestack[6], ply, toplevel); } #ifndef ALLOW_NEXTEM bp = nextem(toplevel); #else if (toplevel) paniced = 0; savebp = nextem(toplevel); panic: if (toplevel) totalcost = 0; bp = savebp; #endif if (bp == 0) { /* * No pattern found, can be pseudo or error * in table. */ if (toplevel) { codep--; DEBUG("pseudo"); dopseudo(); } else goto doreturn; } else { #ifndef NDEBUG chkregs(); #endif if (!toplevel) { ply -= emp - saveemp + 1; if (ply <= 0) ply = 1; } n = *bp++; if (n == 0) { /* "procedure" */ int j, nargs; getint(i, bp); getint(nargs, bp); assert(nargs <= MAXPROCARG); for (j = 0; j < nargs; j++) { getint(procarg[j], bp); } bp = &pattern[i]; n = *bp++; DEBUG("PROC_CALL"); } assert(n > 0 && n <= MAXRULE); if (n > 1) { mindistance = MAXINT; npos = 0; for (i = 0; i < n; i++) { getint(cindex, bp); dist = distance(cindex); #ifndef NDEBUG if (Debug) fprintf(stderr, "distance of pos %d is %u\n", i, dist); #endif if (dist <= mindistance #ifdef ALLOW_NEXTEM || paniced #endif ) { if (dist < mindistance) { if (dist == 0) goto gotit; npos = 0; mindistance = dist; } #ifdef ALLOW_NEXTEM if (dist < MAXINT) #endif pos[npos++] = cindex; } } assert(mindistance < MAXINT); if (npos > 1) { /* * More than 1 tokenpattern is a candidate. * Decision has to be made by lookahead. */ SAVEST; mincost = costlimit - totalcost + 1; assert(mincost <= INFINITY); for (i = 0; i < npos; i++) { t = codegen(&coderules[pos[i]], ply, FALSE, costlimit < MAXINT ? mincost : MAXINT, 0); #ifndef NDEBUG if (Debug) fprintf(stderr, "mincost %u,cost %u,pos %d\n", mincost, t, i); #endif if (t < mincost) { mincost = t; cindex = pos[i]; } RESTST; } FREEST; if (totalcost + mincost > costlimit) { BROKE(); } } else { cindex = pos[0]; } } else { getint(cindex, bp); } gotit: /* * Now cindex contains the code-index of the best candidate * so proceed to use it. */ codep = &coderules[cindex]; } break; } case DO_COERC: { DEBUG("COERC"); tokpatlen = 1; inscoerc = 1; break; } case DO_XXMATCH: DEBUG("XXMATCH"); case DO_XMATCH: { int i, temp; DEBUG("XMATCH"); tokpatlen = (codep[-1] >> 5) & 07; for (i = 0; i < tokpatlen; i++) getint(temp, codep); break; /* match already checked by distance() */ } case DO_MATCH: { int i, j; unsigned mincost, t; token_p tp; int size, lsize; int tokexp[MAXPATLEN]; int nregneeded; token_p regtp[MAXCREG]; c3_p regcp[MAXCREG]; rl_p regls[MAXCREG]; c3_p cp, findcoerc(); #ifdef MAXSPLIT int sret; #endif int stackpad = 0; struct perm *tup, *ntup, *besttup, *tuples(); DEBUG("MATCH"); tokpatlen = (codep[-1] >> 5) & 07; for (i = 0; i < tokpatlen; i++) getint(tokexp[i], codep); tp = &fakestack[stackheight - 1]; i = 0; while (i < tokpatlen && tp >= fakestack) { size = tsize(tp); while (i < tokpatlen && (lsize = ssize(tokexp[i])) <= size) { size -= lsize; i++; } if (i < tokpatlen && size != 0) { totalcost += stackupto(tp, ply, toplevel); CHKCOST(); break; } tp--; } tp = &fakestack[stackheight - 1]; i = 0; while (i < tokpatlen && tp >= fakestack) { size = tsize(tp); lsize = ssize(tokexp[i]); if (size != lsize) { /* find coercion */ #ifdef MAXSPLIT sret = split(tp, &tokexp[i], ply, toplevel); if (sret == 0) { #endif /* MAXSPLIT */ totalcost += stackupto(tp, ply, toplevel); CHKCOST(); break; #ifdef MAXSPLIT } i += sret; #endif /* MAXSPLIT */ } else i += 1; tp--; } nextmatch: tp = &fakestack[stackheight - 1]; i = 0; nregneeded = 0; while (i < tokpatlen && tp >= fakestack) { if (!match(tp, &machsets[tokexp[i]], 0)) { cp = findcoerc(tp, &machsets[tokexp[i]]); #ifndef NDEBUG if (Debug > 1) fprintf(stderr, "findcoerc returns %p at position %d\n", cp, i); #endif if (cp == 0) { for (j = 0; j < nregneeded; j++) regtp[j] -= (tp - fakestack + 1); totalcost += stackupto(tp, ply, toplevel); CHKCOST(); break; } else { if (cp->c3_prop < 0) { totalcost += docoerc(tp, cp, ply, toplevel, 0); CHKCOST(); } else { #ifndef NDEBUG if (Debug > 1) fprintf(stderr, "Register of type %d needed, remembering...\n", cp->c3_prop); #endif assert(nregneeded < MAXCREG); regtp[nregneeded] = tp; regcp[nregneeded] = cp; regls[nregneeded] = curreglist; nregneeded++; } } } i++; tp--; } if (tokpatlen > stackheight) { #ifndef NDEBUG if (Debug > 1) fprintf(stderr, "Pattern too long, %d with only %d items on stack\n", tokpatlen, stackheight); #endif stackpad = tokpatlen - stackheight; for (j = stackheight - 1; j >= 0; j--) fakestack[j + stackpad] = fakestack[j]; for (j = 0; j < stackpad; j++) fakestack[j].t_token = 0; stackheight += stackpad; for (j = 0; j < nregneeded; j++) regtp[j] += stackpad; for (tp = &fakestack[stackpad - 1]; i < tokpatlen && tp >= fakestack; i++, tp--) { cp = findcoerc((token_p)0, &machsets[tokexp[i]]); if (cp == 0) { for (j = 0; j < nregneeded; j++) myfree((string)(regls[j])); #ifndef ALLOW_NEXTEM assert(!toplevel); BROKE(); #else assert(!(toplevel && paniced)); if (paniced) goto normalfailed; totalcost = INFINITY; for (i = 0; i < stackheight - stackpad; i++) fakestack[i] = fakestack[i + stackpad]; stackheight -= stackpad; goto doreturn; #endif } if (cp->c3_prop < 0) { totalcost += docoerc(tp, cp, ply, toplevel, 0); CHKCOST(); } else { assert(nregneeded < MAXCREG); regtp[nregneeded] = tp; regcp[nregneeded] = cp; regls[nregneeded] = curreglist; nregneeded++; } } } else stackpad = 0; assert(i == tokpatlen); if (nregneeded == 0) break; SAVEST; mincost = costlimit - totalcost + 1; tup = tuples(regls, nregneeded); besttup = 0; for (; tup != 0; tup = ntup) { #ifndef NDEBUG if (Debug > 1) { fprintf(stderr, "Next tuple %d,%d,%d,%d\n", tup->p_rar[0], tup->p_rar[1], tup->p_rar[2], tup->p_rar[3]); fprintf(stderr, "totalcost = %u, costlimit = %u, mincost = %u\n", totalcost, costlimit, mincost); } #endif ntup = tup->p_next; for (i = 0, t = 0; i < nregneeded && t < mincost; i++) t += docoerc(regtp[i], regcp[i], ply, FALSE, tup->p_rar[i]); #ifndef NDEBUG if (Debug > 1) fprintf(stderr, "cost after coercions: %u\n", t); #endif if (t < mincost && tokpatlen <= stackheight) { #ifndef NDEBUG if (Debug > 2) fprintf(stderr, "Continuing match after coercions\n"); #endif t += codegen(codep, ply, FALSE, mincost < MAXINT ? mincost - t : MAXINT, 0); } if (t < mincost && tokpatlen <= stackheight) { mincost = t; besttup = tup; } else myfree((string)tup); RESTST; } FREEST; for (i = 0; i < nregneeded; i++) myfree((string)(regls[i])); if (totalcost + mincost > costlimit) { if (besttup) myfree((string)besttup); normalfailed: if (stackpad != tokpatlen) { if (stackpad) { for (i = 0; i < stackheight - stackpad; i++) fakestack[i] = fakestack[i + stackpad]; stackheight -= stackpad; if (costlimit < MAXINT) BROKE(); totalcost += stackupto(&fakestack[stackheight - 1], ply, toplevel); } else totalcost += stackupto(fakestack, ply, toplevel); CHKCOST(); goto nextmatch; } totalcost += mincost; for (i = 0; i < stackheight - stackpad; i++) fakestack[i] = fakestack[i + stackpad]; stackheight -= stackpad; BROKE(); } for (i = 0; i < nregneeded; i++) totalcost += docoerc(regtp[i], regcp[i], ply, toplevel, besttup->p_rar[i]); assert(totalcost <= costlimit); myfree((string)besttup); break; } case DO_TOSTACK: case DO_REMOVE: { int texpno, nodeno; token_p tp; struct reginfo* rp; int doremove = (codep[-1] & 037) == DO_REMOVE; extern int allsetno; DEBUG(doremove ? "REMOVE" : "TOSTACK"); if (codep[-1] & 32) { getint(texpno, codep); getint(nodeno, codep); } else { getint(texpno, codep); nodeno = 0; } if (texpno == allsetno) { totalcost += stackupto(&fakestack[stackheight - tokpatlen - 1], ply, toplevel); CHKCOST(); if (doremove) for (rp = machregs; rp < machregs + NREGS; rp++) rp->r_contents.t_token = 0; break; } for (tp = &fakestack[stackheight - tokpatlen - 1]; tp >= &fakestack[0]; tp--) if (match(tp, &machsets[texpno], nodeno)) { /* investigate possible coercion to register */ totalcost += stackupto(tp, ply, toplevel); CHKCOST(); break; } if (doremove) for (rp = machregs; rp < machregs + NREGS; rp++) { if (rp->r_contents.t_token != 0 && match(&rp->r_contents, &machsets[texpno], nodeno)) { #ifndef NDEBUG if (Debug > 1) fprintf(stderr, "killing reg %ld (%s)\n", (long)(rp - machregs), rp->r_repr ? codestrings[rp->r_repr] : "cc"); #endif rp->r_contents.t_token = 0; } } break; } case DO_KILLREG: case DO_RREMOVE: { /* register remove */ int i, nodeno; token_p tp; tkdef_p tdp; result_t result; int dokill = (codep[-1] & 037) == DO_KILLREG; DEBUG(dokill ? "KILLREG" : "RREMOVE"); getint(nodeno, codep); compute(&enodes[nodeno], &result); if (result.e_typ != EV_REG) break; if (in_stack(result.e_v.e_reg)) BROKE(); /* Check aside-stack */ if (dokill) { /* kill register, and kill condition codes if they are set to this register */ machregs[result.e_v.e_reg].r_contents.t_token = 0; if (machregs[0].r_contents.t_token == -1 && machregs[0].r_contents.t_att[0].ar == result.e_v.e_reg) { machregs[0].r_contents.t_token = 0; } } for (tp = &fakestack[stackheight - tokpatlen - 1]; tp >= &fakestack[0]; tp--) if (tp->t_token == -1) { if (tp->t_att[0].ar == result.e_v.e_reg) goto gotone; } else { tdp = &tokens[tp->t_token]; for (i = 0; i < TOKENSIZE; i++) if (tdp->t_type[i] == EV_REG && tp->t_att[i].ar == result.e_v.e_reg) goto gotone; } break; gotone: /* investigate possible coercion to register */ totalcost += stackupto(tp, ply, toplevel); CHKCOST(); break; } case DO_DEALLOCATE: { int i; tkdef_p tdp; int tinstno; token_t token; DEBUG("DEALLOCATE"); getint(tinstno, codep); instance(tinstno, &token); if (token.t_token == -1) chrefcount(token.t_att[0].ar, -1, TRUE); else { tdp = &tokens[token.t_token]; for (i = 0; i < TOKENSIZE; i++) if (tdp->t_type[i] == EV_REG) chrefcount(token.t_att[i].ar, -1, TRUE); } break; } case DO_REALLOCATE: { struct reginfo* rp; DEBUG("REALLOCATE"); for (rp = machregs + 1; rp < machregs + NREGS; rp++) if (rp->r_tcount) { rp->r_refcount -= rp->r_tcount; rp->r_tcount = 0; } break; } case DO_ALLOCATE: { int i, j; int tinstno; int npos, npos2, pos[NREGS], pos2[NREGS]; unsigned mincost, t; struct reginfo *rp, **rpp; token_t token, token2; int propno; int exactmatch; int decision; if (codep[-1] & 32) { getint(propno, codep); getint(tinstno, codep); DEBUG("ALLOCATE,INIT"); } else { getint(propno, codep); tinstno = 0; DEBUG("ALLOCATE,EMPTY"); } instance(tinstno, &token); if (!forced) { do { npos = exactmatch = 0; for (rpp = reglist[propno]; rp = *rpp; rpp++) if (getrefcount((int)(rp - machregs), FALSE) == 0) { pos[npos++] = rp - machregs; if (eqtoken(&rp->r_contents, &token)) pos2[exactmatch++] = rp - machregs; } /* * Now pos[] contains all free registers with desired * property. If none then some stacking has to take place. */ if (npos == 0) { if (stackheight <= tokpatlen) { if (!toplevel) { BROKE(); } else { if (paniced) fatal("No regs available"); totalcost += stackupto(&fakestack[0], ply, toplevel); goto panic; } } totalcost += stackupto(&fakestack[0], ply, toplevel); CHKCOST(); } } while (npos == 0); if (!exactmatch && tinstno != 0) { /* * No exact match, but we were looking for a particular * token. Now try to find registers of which no * known contents is available (the others might still * be useful). */ for (i = 0; i < npos; i++) if (machregs[pos[i]].r_contents.t_token == 0) { pos2[exactmatch++] = pos[i]; } } if (!exactmatch) { npos2 = npos; for (i = 0; i < npos; i++) pos2[i] = pos[i]; } else { /* * Now we are reducing the number of possible registers. * We take only one equally likely register out of every * equivalence class as given by set of properties. */ npos2 = 0; for (i = 0; i < exactmatch; i++) { pos2[npos2++] = pos2[i]; for (j = 0; j < npos2 - 1; j++) if (eqregclass(pos2[j], pos2[i])) { npos2--; break; } } } /* * Now pos2[] contains all possibilities to try, if more than * one, lookahead is necessary. */ token2.t_token = -1; for (i = 1; i < TOKENSIZE; i++) token2.t_att[i].aw = 0; decision = pos2[0]; if (npos2 != 1) { SAVEST; mincost = costlimit - totalcost + 1; for (j = 0; j < npos2; j++) { chrefcount(pos2[j], 1, FALSE); token2.t_att[0].ar = pos2[j]; allreg[nallreg++] = pos2[j]; if (token.t_token != 0) t = move(&token, &token2, ply, FALSE, mincost); else { t = 0; erasereg(pos2[j]); } if (t < mincost) t += codegen(codep, ply, FALSE, mincost < MAXINT ? mincost - t : MAXINT, 0); if (t < mincost) { mincost = t; decision = pos2[j]; } RESTST; } FREEST; if (totalcost + mincost > costlimit) BROKE(); } } else { decision = forced; if (getrefcount(decision, FALSE) != 0) BROKE(); token2.t_token = -1; } chrefcount(decision, 1, FALSE); token2.t_att[0].ar = decision; if (token.t_token != 0) { totalcost += move(&token, &token2, ply, toplevel, MAXINT); CHKCOST(); } else erasereg(decision); allreg[nallreg++] = decision; break; } case DO_INSTR: { int i, n; int tinstno; token_t token; int stringno; DEBUG("INSTR"); n = ((codep[-1] >> 5) & 07); getint(stringno, codep); if (toplevel) { swtxt(); if (stringno > 10000) { assert(stringno < 10001 + MAXPROCARG); genstr(procarg[stringno - 10001]); } else genstr(stringno); } for (i = 0; i < n; i++) { getint(tinstno, codep); instance(tinstno, &token); if (toplevel) prtoken(&token, i == 0 ? ' ' : ','); if (token.t_token > 0) totalcost += tokens[token.t_token].t_cost.ct_space; } if (toplevel) gennl(); CHKCOST(); break; } case DO_MOVE: { int tinstno; token_t token, token2; DEBUG("MOVE"); getint(tinstno, codep); instance(tinstno, &token); getint(tinstno, codep); instance(tinstno, &token2); totalcost += move(&token, &token2, ply, toplevel, costlimit - totalcost + 1); CHKCOST(); break; } case DO_TEST: { int tinstno; token_t token; DEBUG("TEST"); getint(tinstno, codep); instance(tinstno, &token); totalcost += test(&token, ply, toplevel, costlimit - totalcost + 1); CHKCOST(); break; } case DO_SETCC: { int tinstno; token_t token; DEBUG("SETCC"); getint(tinstno, codep); instance(tinstno, &token); setcc(&token); break; } case DO_ERASE: { int nodeno; result_t result; DEBUG("ERASE"); getint(nodeno, codep); compute(&enodes[nodeno], &result); assert(result.e_typ != EV_INT && result.e_typ != EV_ADDR); if (result.e_typ == EV_REG) { int regno = result.e_v.e_reg; erasereg(regno); } break; } case DO_TOKREPLACE: { int i; int tinstno; int repllen; token_t reptoken[MAXREPLLEN]; DEBUG("TOKREPLACE"); assert(stackheight >= tokpatlen); repllen = (codep[-1] >> 5) & 07; #ifndef NDEBUG if (Debug > 2) fprintf(stderr, "Stackheight=%d, tokpatlen=%d, repllen=%d %s\n", stackheight, tokpatlen, repllen, inscoerc ? "(inscoerc)" : ""); #endif for (i = 0; i < repllen; i++) { getint(tinstno, codep); instance(tinstno, &reptoken[i]); tref(&reptoken[i], 1); } for (i = 0; i < tokpatlen; i++) { if (!inscoerc) tref(&fakestack[stackheight - 1], -1); stackheight--; } for (i = 0; i < repllen; i++) { assert(stackheight < MAXFSTACK); fakestack[stackheight++] = reptoken[i]; } for (i = 0; i < nallreg; i++) chrefcount(allreg[i], -1, FALSE); break; } case DO_EMREPLACE: { int i, j; int nodeno; result_t result[MAXEMREPLLEN]; int emrepllen, eminstr; DEBUG("EMREPLACE"); emrepllen = (codep[-1] >> 5) & 07; j = emp - emlines; if (emrepllen > j) { assert(nemlines + emrepllen - j < MAXEMLINES); for (i = nemlines; i >= 0; i--) emlines[i + emrepllen - j] = emlines[i]; nemlines += emrepllen - j; emp += emrepllen - j; } emp -= emrepllen; for (i = 0; i < emrepllen; i++) { getint(eminstr, codep); getint(nodeno, codep); emp[i].em_instr = eminstr; compute(&enodes[nodeno], &result[i]); } for (i = 0; i < emrepllen; i++) { switch (result[i].e_typ) { default: assert(FALSE); case 0: emp[i].em_optyp = OPNO; emp[i].em_soper = 0; break; case EV_INT: emp[i].em_optyp = OPINT; emp[i].em_soper = tostring(result[i].e_v.e_con); emp[i].em_u.em_ioper = result[i].e_v.e_con; break; case EV_ADDR: emp[i].em_optyp = OPSYMBOL; emp[i].em_soper = ad2str(result[i].e_v.e_addr); break; } } if (!toplevel) { ply += emrepllen; #ifndef NDEBUG if (Debug > 4) fprintf(stderr, "ply becomes %d\n", ply); #endif } break; } case DO_COST: { cost_t cost; DEBUG("COST"); getint(cost.ct_space, codep); getint(cost.ct_time, codep); totalcost += costcalc(cost); CHKCOST(); break; } #ifdef REGVARS case DO_PRETURN: { if (toplevel) { swtxt(); regreturn(); /* in mach.c */ } break; } #endif case DO_RETURN: DEBUG("RETURN"); assert(origcp != startupcode); #ifndef NDEBUG level--; #endif return (totalcost); #ifdef USE_TES case DO_LABDEF: { int index; DEBUG("LABDEF"); getint(index, codep); if (toplevel) { swtxt(); printlabel(index); } break; } #endif } } doreturn: #ifdef ALLOW_NEXTEM if (toplevel && totalcost == INFINITY && !paniced) { DEBUG("PANIC!"); totalcost += stackupto(&fakestack[stackheight - 1], ply, toplevel); #ifndef NDEBUG if (Debug > 2) fprintf(stderr, "Stackheight = %d\n", stackheight); #endif paniced = 1; tokpatlen = 0; goto panic; } #endif #ifndef NDEBUG level--; #endif return (totalcost); } void readcodebytes(void) { #ifndef CODEINC int fd; extern int ncodebytes; if ((fd = open("code", 0)) < 0) { error("Can't open code"); } if (read(fd, coderules, ncodebytes) != ncodebytes) { error("Short read from code"); } close(fd); #endif /* CODEINC */ } #ifdef TABLEDEBUG void initlset(char *f) { set_flag = f; if ((set_fd = open(f + 1, 2)) < 0) error("Can't open %s rw", f + 1); read(set_fd, &set_size, sizeof(int)); set_val = (short*)myalloc(set_size); read(set_fd, set_val, set_size); } void termlset(void) { if (set_fd) { lseek(set_fd, (long)sizeof(int), 0); write(set_fd, set_val, set_size); close(set_fd); if (set_flag[0] == 'u') { int i; fprintf(stderr, "Unused code rules:\n\n"); for (i = 0; i < 8 * set_size; i++) if (set_val[i >> 4] & (1 << (i & 017))) fprintf(stderr, "\"%s\", line %d\n", tablename, i); } } } #endif /* TABLEDEBUG */