/* refine.c,v 1.1 1993/09/17 22:14:00 fbodin Exp */ /* Refinement tests Naming convention: Many of these functions and structures refer to "read iteration" or "write iteration" as if a test were being performed on flow dependence(s), even when the test works for other forms of dependence. */ #include #include #include "include/portable.h" #include #include #include "include/debug.h" #include "include/ip.h" #include "include/lang-interf.h" #include "include/ddomega-build.h" #include "include/ddomega-use.h" #include "include/ddomega.h" #include "include/omega2flags.h" #include "include/kill.h" #include "include/cover.h" #include "include/refine.h" #include "include/missing.h" #include "include/timeTrials.h" /* check to see if a data dependence can be refined at the source, and change d_info if so */ typedef enum { source, dest } which_end; static void refine_end(a_access access_A, a_access access_B, which_end which, dir_and_dist_info *d_info) { uint access_B_nest = accesss_depth(access_B), access_A_nest = accesss_depth(access_A); read_prob_desc rpd; /* write1s = A[i] write2s = A[j] (or B[j] if which == dest) reads = B[k] */ delta_prob_desc dpd; /* original dependence: A[i] --> B[k] */ Problem reads, new_deltas; bool red_complete; var_id sc_vars[maxVars], B_indices[maxVars], B_steps[maxVars]; var_id A_indices[maxVars], A_steps[maxVars]; int NBsteps, NAsteps, Nsc; int l, u, j; dd_in_iterator o; int anyRefinementPossible = 0; int refinementPossible[maxCommonNest]; #if ! defined NDEBUG bool possible_dependence, simplified; #endif #if defined newTimeTrials if (storeResult) refineTests++; #endif assert(!(which == source) || !access_fetch_p(access_A)); assert(!(which == dest) || !access_fetch_p(access_B)); if (omegaPrintResult) { fprintf(debug2, "attempting refinement of %s of dependence\n", which == source ? "source" : "destination"); fprintf(debug2, "\tfrom a store of %s at statement %d\n", access_as_string(access_A), accesss_lineno(access_A)); fprintf(debug2, "\tto a read of %s at statement %d\n", access_as_string(access_B), accesss_lineno(access_B)); fprintf(debug2, "\tdddir = %x, ", d_info->direction); fprintf(debug2, "\trestraint = %x\n", d_info->restraint); } /* First, verify that the dependence isn't constant */ for (j = 1; j <= d_info->nest; j++) if (!d_info->distanceKnown[j]) break; /* if all constant or d_info->nest == 0, no need to refine */ if (j > d_info->nest) { if (omegaPrintResult) { fprintf(debug2, "no non-constant distances to refine\n"); } return; } /* Check which dimensions refinement is feasible in */ for (j = 1; j <= d_info->nest; j++) refinementPossible[j] = 0; o = dd_i_i_for_access((which == source) ? access_A : access_B); while (!dd_i_i_done(o)) { if (dd_i_i_cur_src(o) == ((which == source) ? access_A : access_B)) { /* self output/reduction dependence to end we're refining */ assert(dd_i_i_cur_dest(o) == dd_i_i_cur_src(o)); for (j = 1; j <= d_info->nest; j++) { if (!d_info->distanceKnown[j] && dddirtest(dd_current_dir(dd_i_i_current(o)), ddlt | ddgt, j)) { refinementPossible[j] = 1; anyRefinementPossible = 1; } } } dd_i_i_next(o); } if (!anyRefinementPossible) { if (omegaPrintResult) fprintf(debug2, "Fast break out of refinement\n"); return; } /* BUILD BLACK PROBLEM IN "new_deltas" : i in [A] ^ k in [B] ^ A(i) << B(k) ^ A(i) sub= B(k) */ #if ! defined NDEBUG possible_dependence = #endif build_delta_prob_desc(&dpd, &new_deltas, access_A, access_B, access_A_nest, access_B_nest, d_info->nest); assert(possible_dependence); /* The A(i) << B(k) part */ constrain_with_dd(&new_deltas, &dpd.access2s, &dpd.access1s, d_info, black); /* PART 1: find sets of variables to be used in problem */ NBsteps = NAsteps = Nsc = 0; load_bounds_and_count_steps(access_B, B_indices, B_steps, &NBsteps); load_bounds_and_count_steps(access_A, A_indices, A_steps, &NAsteps); load_constants_for_bounds(access_B, sc_vars, &Nsc); load_constants_for_bounds(access_A, sc_vars, &Nsc); load_constants_for_subscripts(access_B, sc_vars, &Nsc); load_constants_for_subscripts(access_A, sc_vars, &Nsc); if (which == source) { /* PART 2: assign columns to variables */ read_init(&rpd, &reads, sc_and_r, Nsc, sc_vars, access_B_nest, B_indices, NBsteps, B_steps, access_A_nest, A_indices, NAsteps, A_steps, access_A_nest, A_indices, NAsteps, A_steps); #if ! defined NDEBUG read_inv(&rpd, &reads); #endif /* PART 3: set up equations in "reads" */ /* SET UP BLACK PROBLEM IN "reads", IDENTICAL TO THAT IN "new_deltas": i in [A] ^ k in [B] ^ A(i) << B(k) ^ A(i) sub= B(k) */ /* SET UP i in [A] */ bound_indices_and_conditionals(&reads, &rpd.write1s, &rpd.w1steps, &rpd.nonloops, black, access_A); /* SET UP k in [B] */ bound_indices_and_conditionals(&reads, &rpd.reads, &rpd.rsteps, &rpd.nonloops, black, access_B); /* SET UP A(i) sub= B(k) (this can not return 0) */ #if ! defined NDEBUG possible_dependence = #endif equate_subscripts(&reads, &rpd.write1s, &rpd.reads, &rpd.nonloops, black, access_A, access_B); assert(possible_dependence); constrain_with_dd(&reads, &rpd.reads, &rpd.write1s, d_info, black); /* TRY TO SET UP RED PROBLEM IN "reads", EXCEPT REFINEMENT VECTOR PART. IF WE CAN'T ESTABLISH COMPLETE RED BOUNDS, DON'T REFINE IF which == source, RED PROBLEM = j in [A] ^ A(j) sub= B(k) */ red_complete = /* j in [A] ^ A(j) sub= B(k) */ ((equate_subscripts(&reads, &rpd.reads, &rpd.write2s, &rpd.nonloops, red, access_B, access_A) == complete) && (bound_inner_indices_and_conditionals(&reads, &rpd.write2s, &rpd.w2steps, &rpd.nonloops, leading_zeros(d_info->direction, d_info->nest), access_B, red, access_A))); } else { /* PART 2: assign columns to variables */ read_init(&rpd, &reads, sc_and_r, Nsc, sc_vars, access_A_nest, A_indices, NBsteps, A_steps, access_B_nest, B_indices, NBsteps, B_steps, access_B_nest, B_indices, NBsteps, B_steps); #if ! defined NDEBUG read_inv(&rpd, &reads); #endif /* PART 3: set up equations in "reads" */ /* SET UP BLACK PROBLEM IN "reads", IDENTICAL TO THAT IN "new_deltas": i in [A] ^ k in [B] ^ A(i) << B(k) ^ A(i) sub= B(k) */ /* SET UP i in [A] */ bound_indices_and_conditionals(&reads, &rpd.reads, &rpd.rsteps, &rpd.nonloops, black, access_A); /* SET UP k in [B] */ bound_indices_and_conditionals(&reads, &rpd.write2s, &rpd.w2steps, &rpd.nonloops, black, access_B); /* SET UP A(i) sub= B(k) (this can not return 0) */ #if ! defined NDEBUG possible_dependence = #endif equate_subscripts(&reads, &rpd.reads, &rpd.write2s, &rpd.nonloops, black, access_A, access_B); assert(possible_dependence); constrain_with_dd(&reads, &rpd.write2s, &rpd.reads, d_info, black); /* TRY TO SET UP RED PROBLEM IN "reads", EXCEPT REFINEMENT VECTOR PART. IF WE CAN'T ESTABLISH COMPLETE RED BOUNDS, DON'T REFINE IF which == dest, RED PROBLEM = j in [B] ^ A(i) sub= B(j) */ red_complete = /* j in [B] ^ A(i) sub= B(j) */ ((equate_subscripts(&reads, &rpd.write1s, &rpd.write2s, &rpd.nonloops, red, access_B, access_A) == complete) && (bound_inner_indices_and_conditionals(&reads, &rpd.write2s, &rpd.w2steps, &rpd.nonloops, leading_zeros(d_info->direction, d_info->nest), access_A, red, access_B))); } if (!red_complete) { if (omegaPrintResult) { fprintf(debug2, "not checking for refinement - incomplete red bounds\n"); } read_cleanup(&rpd); return; } /* START WITH REFINEMENT VECTOR = DEPENDENCE VECTOR, THEN IN REFINEMENT, WE'LL SHORTEN IT */ if (which == source) { /* SET UP A(j) << B(k) */ constrain_with_dd(&reads, &rpd.reads, &rpd.write2s, d_info, red); } else { /* SET UP A(i) << B(j) */ constrain_with_dd(&reads, &rpd.write1s, &rpd.reads, d_info, red); } /* PART 4: clean up */ #if ! defined NDEBUG read_inv(&rpd, &reads); #endif read_cleanup(&rpd); /* PART 5: TRY TO REFINE DEPENDENCE */ #if ! defined NDEBUG simplified = #endif simplifyProblem(&new_deltas); assert(simplified && "new_deltas can be simplified"); /* ferd */ #if defined newTimeTrials if (storeResult) semiRealRefineTests++; #endif /* go from outer to inner loops, trying to change + to its minimum */ for (j = 1; j <= d_info->nest; j++) { uint e; /* if dddist is unknown, try to refine: see if min distance will cancel out all others */ if (!d_info->distanceKnown[j]) { int fiendish; if (!refinementPossible[j]) break; fiendish = queryVariableBounds(&new_deltas, r_first(&dpd.deltas) - 1 + j, &l, &u); if (fiendish) if (ddextract1(d_info->direction, j) == (ddeq | ddlt)) { l = 0; u = posInfinity; } else { int k; Problem new_new_deltas; problemcpy(&new_new_deltas, &new_deltas); for (k = 1; k <= d_info->nest; k++) { if (k != j) { unprotectVariable(&new_new_deltas, r_first(&dpd.deltas) - 1 + k); } } #if ! defined NDEBUG simplified = #endif simplifyProblem(&new_new_deltas); assert(simplified && "new_new_deltas can be simplified"); /*ferd*/ fiendish = queryVariableBounds(&new_new_deltas, r_first(&dpd.deltas) - 1 + j, &l, &u); if (fiendish) { eliminateRedundant(&new_new_deltas, 0); simplifyProblem(&new_new_deltas); fiendish = queryVariableBounds(&new_new_deltas, r_first(&dpd.deltas) - 1 + j, &l, &u); }; if (fiendish) { eliminateRedundant(&new_new_deltas, 1); simplifyProblem(&new_new_deltas); fiendish = queryVariableBounds(&new_new_deltas, r_first(&dpd.deltas) - 1 + j, &l, &u); }; if (fiendish) { #if defined newTimeTrials if (storeResult) { #endif strange_occurance("Problem still fiendish in refine:\n"); /* debug2 and outputFile should always be the same */ printProblem(&new_new_deltas); #if defined newTimeTrials } #endif break; /* just stop refining */ } } if (l == u) { /* no need to refine, distance is known */ if (l == 0) { dddironly(d_info->direction, ddeq, j); d_info_do_eq(d_info, j) } else if (l > 0) { dddironly(d_info->direction, ddlt, j); } else { dddironly(d_info->direction, ddgt, j); } d_info->distanceKnown[j] = 1; d_info->distance[j] = l; } else if (l != negInfinity && !(skipping_plus_refinement && l > 0)) { int result; Problem tmpProb; problemcpy(&tmpProb, &reads); /* Try to constrain delta index variable #j to exactly l in red equations That is, try to strengthen D in "A(j) < delta = -l --> delta + l = 0 */ constrainVariableValue(&new_deltas, black, r_first(&dpd.deltas) - 1 + j, l); if (which == source) { /* reads must be constrained in terms of read & write */ e = prob_add_zero_EQ(&reads, black); reads._EQs[e].coef[r_first(&rpd.reads) - 1 + j] = 1; reads._EQs[e].coef[r_first(&rpd.write1s) - 1 + j] = -1; reads._EQs[e].coef[0] = -l; e = prob_add_zero_EQ(&reads, red); reads._EQs[e].coef[r_first(&rpd.reads) - 1 + j] = 1; reads._EQs[e].coef[r_first(&rpd.write2s) - 1 + j] = -1; reads._EQs[e].coef[0] = -l; } else { /* reads must be constrained in terms of read & write */ e = prob_add_zero_EQ(&reads, black); reads._EQs[e].coef[r_first(&rpd.write2s) - 1 + j] = 1; reads._EQs[e].coef[r_first(&rpd.reads) - 1 + j] = -1; reads._EQs[e].coef[0] = -l; e = prob_add_zero_EQ(&reads, red); reads._EQs[e].coef[r_first(&rpd.write1s) - 1 + j] = 1; reads._EQs[e].coef[r_first(&rpd.reads) - 1 + j] = -1; reads._EQs[e].coef[0] = -l; } if (!(d_info->direction & ddrefined)) { clone_dd_graph_node_for_refinement(d_info->dd_graph_node_to_be_cloned); d_info->direction |= ddrefined; } if (l == 0) { dddironly(d_info->direction, ddeq, j); d_info_do_eq(d_info, j); } else if (l > 0) { dddironly(d_info->direction, ddlt, j); } else { dddironly(d_info->direction, ddgt, j); } d_info->distanceKnown[j] = 1; d_info->distance[j] = l; } else break; /* minimum distance in loop is neg Infinity */ } } } /* if from_acc is a write, call refine_source if to_acc is a write, call refine_dest (If both are writes, I think we could get a bit more refinement by having a refine_both, which tries to refine either the source or the destination at each loop nest. I also think this would be so rare that its not worth writing the code.) *d_info will be updated if refinement is successful */ void refine_dependence(a_access from, a_access to, dir_and_dist_info *d_info) { /* Read accesses, update operations of the same type as one end of the dependence, and the Entry & Exit nodes can not kill dependences. Thus, avoid refining with them. */ if (!access_fetch_p(from) && !access_update_p(from) && from != Entry) { assert(access_store_p(from)); refine_end(from, to, source, d_info); } if (!access_fetch_p(to) && !access_update_p(to) && to != ExitNode) { assert(access_store_p(to)); refine_end(from, to, dest, d_info); } } /* try to refine a cover or terminator's leading 0+'s into 0's. */ typedef int(*test_for_one)(a_access, a_access, uint, uint, uint, dir_and_dist_info *, char *); static void tighten_cover_or_terminator(a_access from_acc, a_access to_acc, dir_and_dist_info *d_info, char *dd_as_string, test_for_one which_test) { int i; #if ! defined SPEED char str[TINYBUFSIZ + sizeof("tightening of ")]; strcpy(str, "tightening of "); strcat(str, dd_as_string); #else char *str = "Tiny must be compiled without -DSPEED for debugging info"; #endif assert((*which_test)(from_acc, to_acc, accesss_depth(from_acc), accesss_depth(to_acc), accesss_shared_depth(from_acc, to_acc), d_info, dd_as_string)); #if defined newTimeTrials if (storeResult) tightenTests++; #endif /* prototype version - build & solve problem again and again profiling suggests that we don't waste significant time here. */ /* go from outer loops to inner, changing 0+ to 0 */ for (i = 1; i <= d_info->nest; i++) { if (ddextract1(d_info->direction, i) == (ddeq | ddlt)) { /* we have a 0+ */ dir_and_dist_info new_info = *d_info; dddirreset(new_info.direction, ddlt, i); dddirreset(new_info.restraint, ddlt, i); #if defined newTimeTrials if (storeResult) realTightenTests++; #endif if ((*which_test)(from_acc, to_acc, accesss_depth(from_acc), accesss_depth(to_acc), accesss_shared_depth(from_acc, to_acc), &new_info, str)) { if (!(d_info->direction & ddrefined)) { clone_dd_graph_node_for_refinement(d_info->dd_graph_node_to_be_cloned); } d_info->direction = new_info.direction | ddrefined; d_info->restraint = new_info.restraint; d_info->distanceKnown[i] = 1; d_info->distance[i] = 0; } } if (ddextract1(d_info->direction, i) != ddeq) break; } } void tighten_cover(a_access from_acc, a_access to_acc, dir_and_dist_info *d_info, char *dd_as_string) { assert(d_info->direction & ddcovers); tighten_cover_or_terminator(from_acc, to_acc, d_info, dd_as_string, test_for_coverage); } void tighten_terminator(a_access from_acc, a_access to_acc, dir_and_dist_info *d_info, char *dd_as_string) { assert(d_info->direction & ddterminates); tighten_cover_or_terminator(from_acc, to_acc, d_info, dd_as_string, test_for_termination); }