/* ddomega-build.c,v 1.1 1993/09/17 22:13:49 fbodin Exp */ /* Functions that are used in the construction of an omega test problem from a source program. */ #include #include #include #include "include/portable.h" #include "include/debug.h" /* #include "lang-interf.h" #include "ip.h" */ #include "include/affine.h" #include "include/ddomega-build.h" #include "include/Exit.h" var_id *current_set_of_vars; void init_prob(Problem *p, uint Nvars, uint Nprot, char *(*getVarName)(unsigned int, void *), void *getVarNameArgs) { initializeProblem(p); assert(!(Nvars > maxVars)); p->setVarsN(Nvars); p->_safeVars = Nprot; p->setNumEqs(0); p->setNumGEqs(0); p->_getVarName = getVarName; p->_getVarNameArgs = getVarNameArgs; } /* allocate a new _EQs with uninitialized co-efficients */ /* The touched flag only makes a difference on GEQ's */ uint prob_add_EQ(Problem *p, int color) { //++p->_numEQs; p->addNumEqs(1); assert(!(p->getNumEqs() > maxEQs)); p->_EQs[p->getNumEqs() - 1].color = color; return p->getNumEqs() - 1; } /* allocate a new _EQs with all co-efficients 0 */ uint prob_add_zero_EQ(Problem *p, int color) { uint c; //c = p->_numEQs++; c = p->getNumEqs(); p->addNumEqs(1); assert(!(p->getNumEqs() > maxEQs)); eqnnzero(&p->_EQs[c], p->getVarsN()); p->_EQs[c].color = color; return c; } /* allocate a new _GEQs with uninitialized co-efficients */ uint prob_add_GEQ(Problem *p, int color) { p->addNumGEqs(1); assert(!(p->getNumGEqs() > maxGEQs)); p->_GEQs[p->getNumGEqs() - 1].touched = 1; p->_GEQs[p->getNumGEqs() - 1].color = color; return p->getNumGEqs() - 1; } /* allocate a new _GEQs with all co-efficients 0 */ uint prob_add_zero_GEQ(Problem *p, int color) { uint c; //c = p->_numGEQs++; c = p->getNumGEqs(); p->addNumGEqs(1); assert(!(p->getNumGEqs() > maxGEQs)); eqnnzero(&p->_GEQs[c], p->getVarsN()); p->_GEQs[c].touched = 1; p->_GEQs[c].color = color; return c; } /* delta = access1 - access2, so for flow dep, delta = write - read */ void set_deltas(Problem *p, int delta_color, range *deltas, range *a1, range *a2) { uint e, c; assert(r_length(deltas) <= r_length(a1) && r_length(deltas) <= r_length(a2)); for (e = 0; e < r_length(deltas); e++) { c = prob_add_zero_EQ(p, delta_color); p->_EQs[c].coef[e + r_first(deltas)] = 1; p->_EQs[c].coef[e + r_first(a1)] = 1; p->_EQs[c].coef[e + r_first(a2)] = -1; } } /* Constrain a problem with the minimal constraints needed to enforce the direction vector in dd. Use restraint vector unless it is not convex It is also tempting to use the or the direction vector instead if it is "=", but that does not seem to speed things up (?) The loss of speed is less significant if we test for eqs in constrain_with_convex_dddir below. */ void constrain_with_dd(Problem *pr, range *dd_to, range *dd_from, dir_and_dist_info *dd, int color) { int j; for (j = 1; j <= dd->nest; j++) { int thisr; thisr = ddextract1(dd->restraint, j); if (((thisr&ddgt) && (thisr&ddlt) && !(thisr&ddeq)) /* restr = "+-" */ || (ddextract1(dd->direction, j) == ddeq)) /* direc = "0" */ { constrain_with_convex_dddir(pr, dd_to, dd_from, &dd->direction, j, color); } else { constrain_with_convex_dddir(pr, dd_to, dd_from, &dd->restraint, j, color); } } } /* Constrain *pr with the DIRECTION vector in dd. */ void constrain_with_dddirs(Problem *pr, range *dd_to, range *dd_from, dir_and_dist_info *dd, int color) { int j; for (j = 1; j <= dd->nest; j++) { constrain_with_convex_dddir(pr, dd_to, dd_from, &dd->direction, j, color); } } /* Constrain *pr with dimension j of *dir. Use equations of color "color". */ void constrain_with_convex_dddir(Problem *pr, range *dd_to, range *dd_from, dddirection *dir, int j, int color) { int e; if (ddextract1(*dir, j) == ddeq) { e = prob_add_zero_EQ(pr, color); pr->_EQs[e].coef[r_first(dd_to) - 1 + j] = -1; pr->_EQs[e].coef[r_first(dd_from) - 1 + j] = 1; return; } if (!dddirtest(*dir, ddlt, j)) { /* no +, so dd_to - dd_from <= 0 or < 0 ie, dd_from - dd_to >= 0 or > 0 ie, dd_from - dd_to >= 0 or ... -1 >= 0 */ e = prob_add_zero_GEQ(pr, color); pr->_GEQs[e].coef[r_first(dd_to) - 1 + j] = -1; pr->_GEQs[e].coef[r_first(dd_from) - 1 + j] = 1; pr->_GEQs[e].coef[0] = -!dddirtest(*dir, ddeq, j); } if (!dddirtest(*dir, ddgt, j)) { /* no -, so dd_to - dd_from >= 0 or > 0 ie, dd_to - dd_from >= 0 or ... -1 >= 0 */ e = prob_add_zero_GEQ(pr, color); pr->_GEQs[e].coef[r_first(dd_to) - 1 + j] = 1; pr->_GEQs[e].coef[r_first(dd_from) - 1 + j] = -1; pr->_GEQs[e].coef[0] = -!dddirtest(*dir, ddeq, j); } } /* For each term in ae, add sign * its co-efficient to e. Declarations of symbolic constants must have been tagged with their column number already Colunm numbers of indices are their depth + r_first(indices) */ static void add_coefficients(Eqn e, range *indices, int sign, affine_expr *ae) { int v; assert(ae->terms[0].tiny_var == NIL); e->coef[0] += sign * ae->terms[0].coefficient; for (v = 1; v < ae->nterms; v++) { if (var_id_index_p(ae->terms[v].tiny_var)) { /* index */ int loop = var_ids_loop_no(ae->terms[v].tiny_var); e->coef[r_first(indices) - 1 + loop] += sign * ae->terms[v].coefficient; } else { /* sym. const */ assert(var_id_const_p(ae->terms[v].tiny_var)); e->coef[var_ids_tag(ae->terms[v].tiny_var)] += sign * ae->terms[v].coefficient; } } } /* for the lower bound, lb <= i === i - lb >= 0 for the upper bound, i <= ub === -i + ub >= 0 define bound types so that establish_one_bound works out... */ typedef enum { upper = 1, lower = -1 } which_bound; /* return 1 if boundable, 0 if not affine */ static int establish_one_bound(Problem *p, int color, range *indices, which_bound b, affine_expr *ae, uint depth) { if (!is_affine(ae)) return 0; while (ae != NIL) { uint c; /* new constraint */ assert(is_affine(ae)); c = prob_add_zero_GEQ(p, color); add_coefficients(&p->_GEQs[c], indices, b, ae); //assert(p->_GEQs[c].coef[r_first(indices) - 1 + depth] == 0); if (p->_GEQs[c].coef[r_first(indices) - 1 + depth] != 0) return 0; p->_GEQs[c].coef[r_first(indices) - 1 + depth] = -b; ae = ae->other_branch; } return 1; } /* start_expr should point to the 1st expression taken on by the loop return 1 if bound, 0 if not */ static int handle_nonunit_increments(Problem *p, range *indices, int *which_step, int color, loop_context l, sint incr, uint depth) { affine_expr *start_expr = loop_start(l); assert(incr >= 1); /* can't handle any others yet */ if (abs(incr) == 1) return 1; /* if lower bound is linear AND HAS NO "MAX" add the constraint: index = lower_bound + gensym * step otherwise, don't add any new constraints, unless color == red */ assert(start_expr != NIL); if (is_affine(start_expr) && start_expr->other_branch == NIL) { int c, v, i2; c = prob_add_zero_EQ(p, color); add_coefficients(&p->_EQs[c], indices, 1, start_expr); /* we now have the EQ "lower_bound = 0" */ //assert(p->_EQs[c].coef[r_first(indices) - 1 + depth] == 0); if (p->_EQs[c].coef[r_first(indices) - 1 + depth] != 0) return 0; p->_EQs[c].coef[r_first(indices) - 1 + depth] = -1; /* we now have the EQ "lower_bound - index = 0" */ /* we can reduce the co-efficients to their value mod(step), since we're adding gensym()*step */ i2 = incr / 2; for (v = 1; v <= p->getVarsN(); v++) { int tmp = intMod(p->_EQs[c].coef[v], incr); if (tmp > i2) tmp -= incr; p->_EQs[c].coef[v] = tmp; } p->_EQs[c].coef[*which_step] = incr; (*which_step)++; return 1; } else { return 0; } } /* Create constraints in prob for the loop index whose dolimit is pointed to by limits. May add one or more(min or max) upper and lower constraints May add an equality constraint if the increment != 0 return 1 if completely bound, 0 if we had any non-affine stuff */ static int bound_index(Problem *p, int color, loop_context limits, range *indices, range *non_i, int *which_step, uint depth) { sint incr; bool known; incr = 1; known = 1; if (loop_has_step_p(limits)) { /* there is an increment expression */ loops_step(limits, &incr, &known); } if (!(known && incr >= 1)) ErrAssert("DO loop step must be positive"); if (known) { if (incr > 0) { int l, u, i; l = establish_one_bound(p, color, indices, lower, loop_start(limits), depth); u = establish_one_bound(p, color, indices, upper, loop_end(limits), depth); i = handle_nonunit_increments(p, indices, which_step, color, limits, incr, depth); return l && u && i; } else if (incr < 0) { /* assertion checking is off */ int l, u, i; fprintf(stderr, "can't handle negative step yet\n"); Exit(2); l = establish_one_bound(p, color, indices, lower, loop_end(limits), depth); u = establish_one_bound(p, color, indices, upper, loop_start(limits), depth); i = handle_nonunit_increments(p, indices, which_step, color, limits, incr, depth); return l && u && i; } else { /* assertion checking is off */ fprintf(stderr, "What do you mean by having a step of 0? What are you, stupid?\n"); Exit(1); return 0; } } else { /* assertion checking is off */ fprintf(stderr, "can't handle unknown step\n"); Exit(2); return 0; } } /* Create constraints in problem for the conditional. May add any number of GEQ or EQ constraints return 1 if completely bound, 0 if we had any non-affine stuff This initial version only works with the puny subset of conditionals described in affine.c, and is tiny-specific. A conservative tiny-free alternative is to just return 0. */ static struct { int lhs_sign; int rhs_sign; int constant; } conditionals[4] = { { 1, -1, -1 }, /* gt */ { 1, -1, 0 }, /* ge */ { -1, 1, -1 }, /* lt */ { -1, 1, 0 } /* le */ }; #define N_ELEMENTS(ARRAY) ( sizeof(ARRAY)/sizeof(ARRAY[0]) ) static int puny_bound_condition(Problem *p, int color, if_context cond, range *indices, range *non_i) { if (if_condition_ok(cond)) { int c, kind; kind = if_compare_op(cond); if (kind < 0 || kind >= N_ELEMENTS(conditionals)) { assert(!"What the heck? How did that get to be affine?"); return 0; } if (if_else_branch(cond)) { kind = N_ELEMENTS(conditionals) - kind; } else { #if ! defined NOT_TINY assert(cond->nodeop == op_then || cond->nodeparent->nodeop == op_assert); #endif } c = prob_add_zero_GEQ(p, color); add_coefficients(&p->_GEQs[c], indices, conditionals[kind].lhs_sign, if_compare_left(cond)); add_coefficients(&p->_GEQs[c], indices, conditionals[kind].rhs_sign, if_compare_right(cond)); p->_GEQs[c].coef[0] += conditionals[kind].constant; #if ! defined NOT_TINY assert(!cond->nodeparent->nodeaffine->other_branch->other_branch); #endif return 1; } else { return 0; } } /* Establish bounds for the loop indices and conditionals that enclose node n. If we come across a non-affine expression return 0. If we successfully bound everything return 1. Note that we finish doing all the bounds we can, in either case, so zapping will work well. Assume that "outer" outer loops have been taken care of in some other way - i.e. that we are building the red part of some problem which represents a dependence with "outer" leading 0's, so we will take care of these variables by setting them equal to the black loop indices. If "skip_outer_ifs_containing_me" is non-nil, any ifs that are within exactly "outer" loops and contain it. This corresponds to the situation: for1 for2 if1 then if2a then access a endif if2b then access skip_outer_ifs_containing_me endif endif endfor endfor in which we want to skip if1 when bounding a in the 0,0 dependence from a to skip_outer_ifs_containing_me. Note that we don't have to distinguish between the then & else parts, as we can't have a 0,0 if one access is is each. */ int bound_inner_indices_and_conditionals(Problem *p, range *indices, range *steps, range *non_i, int outer, a_access skip_outer_ifs_containing_me, int color, a_access a) { int result; int which_step = r_first(steps); context_iterator c; if (a == Entry || a == ExitNode) return(1); c = cont_i_for_access(a); result = 1; while (!cont_i_done(c)) { if (cont_i_cur_loop_p(c)) { if (cont_i_cur_depth(c) <= outer) return result; #if ! defined NOT_TINY assert(do_op(c->nodeparent->nodeop)); #endif if (!bound_index(p, color, cont_i_cur_loop(c), indices, non_i, &which_step, cont_i_cur_depth(c))) { assert(which_step <= r_last(steps) + 1); result = 0; } } else { assert(cont_i_cur_if_p(c)); #if ! defined NOT_TINY assert(c->nodeparent->nodeop == op_if); #endif if (!(skip_outer_ifs_containing_me && cont_i_cur_depth(c) <= outer && access_is_in_then_or_else_of(skip_outer_ifs_containing_me, c)) && !puny_bound_condition(p, color, cont_i_cur_if(c), indices, non_i)) { result = 0; } } cont_i_next(c); } assert(which_step <= r_last(steps) + 1); #if ! defined NOT_TINY for (c = Assertions; c; c = c->nodelink) { assert(c->nodeop == op_assert); if (!puny_bound_condition(p, color, c->nodechild, indices, non_i)) { result = 0; } } #endif return result; } /* adjust the problem to include equality of subscript expressions at nodes access1 and access2. Index variables for access1 are looked up in range i1, and those for access2 in range i2. The non-index variables are looked up in the range non_i. returns 0 if subscripts could not possibly be equal. In this case, we may not finish adding the equalities. returns 1 if the conditions for their equality have been completely described by the constraints added to p returns 2 if there was a non-affine expression, in which case the constraints added to p must be true for equality (but they may also be true for some non-equal cases). If color==red, we FINISH trying to bound subscripts (for zapping). */ equate_descr equate_subscripts(Problem *p, range *i1, range *i2, range *non_i, int color, a_access access1, a_access access2) { sub_iterator sub1, sub2; int is_complete = complete; if (access1 == Entry || access1 == ExitNode || access2 == Entry || access2 == ExitNode) return (equate_descr)1; /* we assume children of the node are subscripts, so scalars MUST have no nodechildren assert(array_op(access1->nodeop) || access1->nodechild == NIL); */ sub1 = sub_i_for_access(access1); sub2 = sub_i_for_access(access2); while (!sub_i_done(sub1)) { assert(!sub_i_done(sub2)); /* must have same # of subscripts */ assert(sub_i_cur_affine(sub1) != NIL && sub_i_cur_affine(sub2) != NIL); if (sub_i_cur_is_affine(sub1) && sub_i_cur_is_affine(sub2)) { uint c; /* new constraint # */ int i; assert((sub_i_cur_affine(sub1)->other_branch == NIL) && (sub_i_cur_affine(sub2)->other_branch == NIL)); c = prob_add_zero_EQ(p, color); add_coefficients(&p->_EQs[c], i1, 1, sub_i_cur_affine(sub1)); add_coefficients(&p->_EQs[c], i2, -1, sub_i_cur_affine(sub2)); for (i = p->getVarsN(); i > 0; i--) if (p->_EQs[c].coef[i] != 0) break; if (i == 0) { /* linearity = 0 */ if (p->_EQs[c].coef[0] != 0) return not_possible; /* never equal */ //p->_numEQs--; /* always equal: don't add equation */ p->addNumEqs(-1); } } else { is_complete = partial; } sub_i_next(sub1); sub_i_next(sub2); } assert(sub_i_done(sub2)); /* must have same # of subscripts */ return (equate_descr)is_complete; } /* postcondition: n < maxVars */ static void NeedNVars(int n) { if (n >= maxVars) { fprintf(debug2, "Out of Omega variables\n"); Exit(2); } } /* set bounds[1...depth(n)] to point to the dolimits of the loops containing n set *Nsteps to the # of loops that have step expressions set steps[0 ... *Nsteps - 1] to NIL */ void load_bounds_and_count_steps(a_access a, var_id bounds[], var_id steps[], int *Nsteps) { context_iterator c; if (a == Entry || a == ExitNode) return; c = cont_i_for_access(a); while (!cont_i_done(c)) { if (cont_i_cur_loop_p(c)) { bounds[cont_i_cur_depth(c)] = loop_var_id(cont_i_cur_loop(c)); if (loop_has_step_p(cont_i_cur_loop(c))) { /* handle step */ NeedNVars(*Nsteps); steps[*Nsteps] = 0; (*Nsteps)++; } } else { assert(cont_i_cur_if_p(c)); /* handle if trivially - no new bounds or steps */ } cont_i_next(c); } } typedef struct { var_id *consts; int *Nconsts; } const_stuff; static void load_a_const(var_id v, void *args) { const_stuff *cs = (const_stuff *)args; if (var_id_const_p(v)) { /* not a loop index */ if (var_ids_tag(v) == UNTAGGED) { NeedNVars(*cs->Nconsts); var_ids_tag(v) = *cs->Nconsts; cs->consts[(*cs->Nconsts)++] = v; } else { assert(cs->consts[var_ids_tag(v)] == v); } } else { assert(var_id_index_p(v)); } } #if 0 static void load_consts_for_expr(node *expr, var_id consts[], int *Nconsts) { node *nn; for (nn = expr; nn != NIL; nn = nn->nodenext) { assert(!array_op(nn->nodeop)); switch (nn->nodeop) { case op_fetch: case op_store: if (get_nodevalue_node(nn)->nodeop == op_declare) { /* not a loop index */ if (get_nodevalue_sym(get_nodevalue_node(nn))->symtag == UNTAGGED) { NeedNVars(*Nconsts); get_nodevalue_sym(get_nodevalue_node(nn))->symtag = *Nconsts; consts[(*Nconsts)++] = get_nodevalue_sym(get_nodevalue_node(nn)); } else { assert(consts[get_nodevalue_sym(get_nodevalue_node(nn)) ->symtag] == get_nodevalue_sym(get_nodevalue_node(nn))); } } else { assert(get_nodevalue_node(nn)->nodeop == op_dolimit); } break; default: load_consts_for_expr(nn->nodechild, consts, Nconsts); } } } #endif /* ensure that all symbolic constants used in affine subscript expressions appear in consts[0..*Nconsts-1], and that they are tagged with their indices. If called with a scalar, do nothing */ void load_constants_for_subscripts(a_access a, var_id consts[], int *Nconsts) { const_stuff cs; sub_iterator sub; if (a == Entry || a == ExitNode) return; cs.consts = consts; cs.Nconsts = Nconsts; sub = sub_i_for_access(a); while (!sub_i_done(sub)) { sub_i_map_over_cur_vars(sub, &load_a_const, &cs); sub_i_next(sub); } } /* same for affine expressions used in loop bounds of loops surrounding n */ void load_constants_for_bounds(a_access a, var_id consts[], int *Nconsts) { const_stuff cs; context_iterator c; if (a == Entry || a == ExitNode) return; cs.consts = consts; cs.Nconsts = Nconsts; c = cont_i_for_access(a); while (!cont_i_done(c)) { if (cont_i_cur_loop_p(c)) { loop_map_over_start_vars(cont_i_cur_loop(c), &load_a_const, &cs); loop_map_over_end_vars(cont_i_cur_loop(c), &load_a_const, &cs); } else { assert(cont_i_cur_if_p(c)); if_map_over_vars(cont_i_cur_if(c), &load_a_const, &cs); } cont_i_next(c); } #if ! defined NOT_TINY for (c = Assertions; c; c = c->nodelink) { if_map_over_vars(c->nodechild, &load_a_const, &cs); } #endif }