# include # include # include # include "globs.h" # include # include SCCSID(@(#)aggregate.c 8.3 2/8/85) /* ** AGGREGATE - replace aggregates with their values ** ** Aggregate attempts to optimize aggregate processing ** wherever possible. It replaces aggregates with their ** values, and links aggregate functions which have ** identical by-lists together. ** ** Note that for the sake of this code, A "prime" ** aggregate is one in which duplicates are removed. ** These are COUNTU, SUMU, and AVGU. ** ** Aggregate first forms a list of all aggregates in ** the order they should be processed. ** ** For each aggregate, it looks at all other aggregates ** to see if there are two simple aggregates ** or if there is another aggregate funtion with the ** same by-list. ** ** An attempt is made to run ** as many aggregates as possible at once. This can be ** done only if two or more aggregates have the same ** qualifications and in the case of aggregate functions, ** they must have identical by-lists. ** Even then, certain combinations ** of aggregates cannot occure together. The list is ** itemized later in the code. ** ** Aggregate calls BYEVAL or AGEVAL to actually process ** aggregate functions or aggregates respectively. ** ** Trace Flags: ** 40 */ aggregate(root) QTREE *root; { struct agglist alist[MAXAGG + 1]; QTREE *rlist[MAXAGG + 1]; struct agglist *al, *al1; register QTREE *agg, *aop1, *r; QTREE *aop, *agg1; int i, simple_agg, varmap; int attcnt, anyagg, attoff, twidth; QTREE *makavar(), *agspace(); extern char *rangename(); extern QTREE *ageval(); extern QTREE *byeval(); al = alist; De.de_aggnext = al; De.de_agglim = &al[MAXAGG]; findagg(&root, root); /* generate list of all aggregates */ De.de_aggnext->agpoint = 0; /* mark end of the list */ anyagg = 0; varmap = root->sym.value.sym_root.lvarm | root->sym.value.sym_root.rvarm; /* process each aggregate */ for (;agg = al->agpoint; al++) { /* is this still an aggregate? */ if (agg->sym.type != AGHEAD) continue; mapvar(agg, 0); /* map the aggregate tree */ anyagg++; De.de_sourcevar = bitpos(agg->sym.value.sym_root.lvarm | agg->sym.value.sym_root.rvarm); # ifdef xDTR1 if (tTf(40, 4)) printf("De.de_sourcevar=%d,rel=%s\n", De.de_sourcevar, rangename(De.de_sourcevar)); # endif simple_agg = (agg->left->sym.type == AOP); /* TRUE if no bylist */ aop = agg->left; /* assume it was TRUE */ # ifdef xDTR1 if (tTf(40, 0)) printf("%s\n", simple_agg ? "agg" : "agg-func"); # endif if (simple_agg) { /* simple aggregate */ rlist[0] = agspace(aop); twidth = aop->sym.value.sym_op.opfrml & I1MASK; /* init width to the width of the aggregate */ } else { attoff = agg->left->left->sym.value.sym_resdom.resno + 2; aop = aop->right; /* find the AOP node */ /* assign new source variable for aggregate */ al->agvarno = getrange(&varmap); /* compute width of bylist + count field */ twidth = findwid(agg->left) + 4; /* make sure the aggregate does not exceed max dimensions */ if (chkwidth(aop, &twidth, attoff)) derror(AGFTOBIG); } attcnt = 1; /* one aggregate so far */ /* look for another identical aggregate */ for (al1 = al + 1; agg1 = al1->agpoint; al1++) { /* if agg is nested inside agg1 then ignore it */ if (al->agfather == agg1 || agg1->sym.type != AGHEAD) { continue; } /* split aggs and agg-func apart */ /* check for identical aggregate */ if (simple_agg) { aop1 = agg1->left; /* find AOP node */ if (aop1->sym.type != AOP) continue; /* not a simple agg */ /* make sure they can be run together */ if (checkagg(agg, aop, agg1, aop1) == 0) continue; if ((i = sameagg(agg, aop1, attcnt)) >= 0) { /* found identical aggregate to rlist[i] */ r = rlist[i]; } else { /* put this agg in with the others */ /* first make sure it won't exceed tuple length */ if (chkwidth(aop1, &twidth, 0)) continue; /* can't be included */ r = rlist[attcnt++] = agspace(aop1); /* connect into tree */ aop1->left = agg->left; agg->left = aop1; } } else { /* aggregate function */ if (!sameafcn(agg->left->left, agg1->left->left)) continue; aop1 = agg1->left->right; /* find AOP */ if (checkagg(agg, aop, agg1, aop1) == 0) { /* same by-lists but they can't be run together */ continue; } if ((i = sameagg(agg, aop1, attcnt)) < 0) { /* make sure there is room */ if (chkwidth(aop1, &twidth, attcnt + attoff)) continue; /* add aggregate into tree */ i = attcnt++; aop1->left = agg->left->right; agg->left->right = aop1; } r = makavar(aop1, al->agvarno, i + attoff); } /* replace aggregate in original tree with its value */ *(al1->father) = r; /* remove aggregate from local list */ agg1->sym.type = -1; # ifdef xDTR1 if (tTf(40, 3)) printf("including aghead %x\n", agg1); # endif } /* process aggregate */ if (simple_agg) { rlist[attcnt] = 0; ageval(agg, rlist); /* pass tree and result list */ r = rlist[0]; } else { opt_bylist(alist, agg); byeval(al->agfather, agg, al->agvarno); r = makavar(aop, al->agvarno, attoff); } /* ** Link result into tree. al->father hold the address ** &(tree->left) or &(tree->right). */ *(al->father) = r; # ifdef xDTR1 if (tTf(40, 4)) { printf("agg value\n"); treepr(*(al->father)); } # endif } if (anyagg) { opt_bylist(alist, root); mapvar(root, 0); /* remap main tree */ } } /* ** findagg builds a list of all aggregates ** in the tree. It finds them by leftmost ** innermost first. ** ** The parameters represent: ** nodep: the address of the node pointing to you ** eg. &(tree->left) or &(tree->right) ** agf: the root node. or if we are inside ** a nested aggregate, the AGHEAD node */ findagg(nodep, agf) QTREE **nodep; QTREE *agf; { register QTREE *q, *f; register struct agglist *list; int agg; if ((q = *nodep) == NULL) return; f = agf; if (agg = (q->sym.type == AGHEAD)) f = q; /* this aggregate is now the father root */ /* find all aggregates below */ findagg(&(q->left), f); findagg(&(q->right), f); /* if this is an aggregate, put it on the list */ if (agg) { if (De.de_aggnext >= De.de_agglim) derror(TOOMANYAGGS); list = De.de_aggnext; list->father = nodep; list->agpoint = q; list->agfather = agf; De.de_aggnext++; } } /* ** Agspace allocates space for the result of ** a simple aggregate. */ QTREE * agspace(aop) QTREE *aop; { register QTREE *a, *r; register int length; extern char *need(); a = aop; length = a->sym.value.sym_op.opfrml & I1MASK; r = (QTREE *) need(De.de_qbuf, length + QT_HDR_SIZ); r->left = r->right = 0; r->sym.type = a->sym.value.sym_op.opfrmt; r->sym.len = length; return (r); } /* ** Chkwidth -- make sure that the inclusion of another aggregate will ** not exceed the system limit. This means that the total width ** cannot exceed MAXTUP and the number of domains cannot exceed MAXDOM-1 */ chkwidth(aop, widthp, domno) QTREE *aop; int *widthp; int domno; { register int width; width = *widthp; # ifdef xDTR1 if (tTf(40, 10)) printf("agg width %d,dom %d\n", width, domno); # endif width += (aop->sym.value.sym_op.opfrml & I1MASK); if (width > MAXTUP || domno > MAXDOM - 1) return (1); *widthp = width; return (0); } /* ** Determine whether an aggregate is prime ** or a don't care aggregate. Returns TRUE ** if COUNTU,SUMU,AVGU,MIN,MAX,ANY. ** Returns false if COUNT,SUM,AVG. */ cprime(aop) QTREE *aop; { register int i; i = TRUE; switch (aop->sym.value.sym_op.opno) { case opCOUNT: case opSUM: case opAVG: i = FALSE; } return (i); } /* ** Getrange find a free slot in the range table ** for an aggregate function. ** ** If there are no free slots,derror is called */ getrange(varmap) int *varmap; { register int i, map, bit; map = *varmap; for (i = 0; i < MAXRANGE; i++) { /* if slot is used, continue */ if ((bit = 1 << i) & map) continue; map |= bit; /* "or" bit into the map */ *varmap = map; # ifdef xDTR1 if (tTf(40, 10)) printf("Assn var %d, map %o\n", i, map); # endif return (i); } derror(NODESCAG); return (-1); } checkagg(aghead1, aop1, aghead2, aop2) QTREE *aghead1; QTREE *aop1; QTREE *aghead2; QTREE *aop2; { register QTREE *aop_1, *aop_2, *agg1; int ok; /* two aggregate functions can be run together ** according to the following table: ** ** prime !prime don't care ** ** prime afcn? never afcn? ** !prime never always always ** don't care afcn? always always ** ** don't care includes: ANY, MIN, MAX ** afcn? means only if a-fcn's are identical */ aop_1 = aop1; aop_2 = aop2; agg1 = aghead1; if (!prime(aop_1) && !prime(aop_2)) ok = TRUE; else if (sameafcn(aop_1->right, aop_2->right)) ok = cprime(aop_1) && cprime(aop_2); else ok = FALSE; /* The two aggregates must range over the same variables */ if ((agg1->sym.value.sym_root.lvarm | agg1->sym.value.sym_root.rvarm) != (aghead2->sym.value.sym_root.lvarm | aghead2->sym.value.sym_root.rvarm)) ok = FALSE; /* check the qualifications */ if (ok) ok = sameafcn(agg1->right, aghead2->right); return (ok); } sameagg(aghead, newaop, agg_depth) QTREE *aghead; QTREE *newaop; int agg_depth; { register QTREE *agg, *newa; register int i; agg = aghead; newa = newaop; agg = agg->left; if (agg->sym.type == BYHEAD) agg = agg->right; /* agg now points to first aggregate */ for (i = 1; agg; agg = agg->left, i++) if (sameafcn(agg->right, newa->right) && agg->sym.value.sym_resdom.resno == newa->sym.value.sym_op.opno) { # ifdef xDTR1 if (tTf(40, 6)) printf("found identical aop %x\n", newa); # endif return (agg_depth - i); } /* no match */ return (-1); } opt_bylist(alist, root) struct agglist *alist; QTREE *root; { register struct agglist *al; register QTREE *agg; register struct hitlist *hl; QTREE **tpr, *tree, *lnodv[MAXDOM+2]; struct hitlist hlist[30]; int anyop, i, usedmap, vars, treemap; /* compute bitmap of all possible vars in tree (can include xtra vars) */ treemap = root->sym.value.sym_root.lvarm | root->sym.value.sym_root.rvarm; anyop = FALSE; /* scan the list of aggregates looking for one nested in root */ for (al = alist; (agg = al->agpoint) && agg != root; al++) { if (agg->sym.type == AGHEAD && agg->left->sym.type == BYHEAD && al->agfather == root) { /* this aggregate function is nested in root */ /* make sure it has some vars of interest */ if ((treemap & varfind(agg->left->left, (QTREE *)NULL)) == 0) continue; # ifdef xDTR1 if (tTf(40, 11)) { printf("nested agg\n"); treepr(agg); } # endif /* form list of bydomains */ lnodv[lnode(agg->left->left, lnodv, 0)] = 0; usedmap = 0; De.de_hnext = &hlist[0]; De.de_hlimit = &hlist[30]; /* find all possible replacements */ vars = modtree(&root, lnodv, &usedmap); /* ** All references to a variable must be replaced ** in order to use this aggregates by-domains. */ if (usedmap && ((vars & usedmap) == 0)) { # ifdef xDTR1 if (tTf(40, 7)) printf("Committed\n"); # endif /* success. Committ the tree changes */ De.de_hnext->trepr = NULL; for (hl = &hlist[0]; tpr = hl->trepr; hl++) { /* get bydomain number */ i = hl->byno; /* get node being replaced */ tree = *tpr; /* if it is already a var, just change it */ if (tree->sym.type == VAR) { tree->sym.value.sym_var.varno = al->agvarno; tree->sym.value.sym_var.attno = i + 2; } else *tpr = makavar(lnodv[i], al->agvarno, i + 2); anyop = TRUE; # ifdef xDTR1 if (tTf(40, 7)) { printf("modified tree\n"); treepr(*tpr); } # endif } } /* vars is now a map of the variables in the root */ treemap = vars; } } /* if any changes were made, get rid of the unnecessary links */ if (anyop) chklink(root); } modtree(pnode, lnodv, replmap) QTREE **pnode; QTREE *lnodv[]; int *replmap; { register QTREE *tree; register int vars, i; QTREE *afcn; /* point up current node */ if ((tree = *pnode) == NULL) return (0); /* examine each by-list for match on this subtree */ for (i = 0; afcn = lnodv[i]; i++) { if (sameafcn(tree, afcn->right)) { # ifdef xDTR1 if (tTf(40, 9)) { printf("potential Jackpot"); treepr(tree); } # endif vars = varfind(tree, (QTREE *)NULL); if (De.de_hnext == De.de_hlimit) return (vars); /* no more room */ /* record what needs to be done */ De.de_hnext->trepr = pnode; De.de_hnext->byno = i; De.de_hnext++; *replmap |= vars; return (0); } } if (tree->sym.type == VAR) return (01 << tree->sym.value.sym_var.varno); /* try the subtrees */ vars = modtree(&(tree->left), lnodv, replmap); if ((vars & *replmap) == 0) vars |= modtree(&(tree->right), lnodv, replmap); return (vars); } chklink(root) QTREE *root; { register QTREE *r, *b, *last; last = root; for (r = last->right; r->sym.type != QLEND; r = r->right) { /* if this is an EQ node then check for an unnecessary compare */ if ((b = r->left)->sym.type == BOP && b->sym.value.sym_op.opno == opEQ) { if (sameafcn(b->left, b->right)) { # ifdef xDTR1 if (tTf(40, 5)) { printf("unnec clause\n"); treepr(b); } # endif last->right = r->right; continue; } } last = r; } } sameafcn(q1, q2) QTREE *q1, *q2; { register QTREE *t1, *t2; register int len; int type; t1 = q1; t2 = q2; if (!(t1 && t2)) return(!(t1 || t2)); len = (t1->sym.len & I1MASK) + SYM_HDR_SIZ; type = t1->sym.type; if (type == VAR) len = sizeof(struct varnode); if (type == AND) len = 2; if (!bequal(&t1->sym.type, &t2->sym.type, len)) return(0); return(sameafcn(t1->left,t2->left) && sameafcn(t1->right,t2->right)); } /* ** varfind -- find all variables in the tree pointed to by "root". ** Examine all parts of the tree except aggregates. For ** aggregates, ignore simple aggregate and look only ** at the by-lists of aggregate functions. If the aggregate ** is "aghead" then ignore it. There is no reason to look ** at yourself!!!! ** This routine is called by byeval() to determine ** whether to link the aggregate to the root tree. ** ** Curiosity note: since the tree being examined has not been ** processed by decomp yet, ckvar does not need to be called ** since the var could not have been altered. */ varfind(root, aghead) QTREE *root; QTREE *aghead; { register QTREE *tree; register int type; if ((tree = root) == NULL) return (0); if ((type = tree->sym.type) == AGHEAD) { /* ignore if it matches aghead */ if (tree == aghead) return (0); /* if this is an aggregate function, look at bylist */ tree = tree->left; if ((type = tree->sym.type) != BYHEAD) return (0); } if (type == VAR) return (1 << tree->sym.value.sym_var.varno); return (varfind(tree->left, aghead) | varfind(tree->right, aghead)); }