/* Analyze loop dependencies Copyright (C) 2000, 2002 Free Software Foundation, Inc. This file is part of GCC. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* References: Practical Dependence Testing, Goff, Kennedy, Tseng, PLDI, 1991 High Performance Compilers for Parallel Computing, Wolfe */ #include "config.h" #include "system.h" #include "rtl.h" #include "expr.h" #include "tree.h" #include "c-common.h" #include "flags.h" #include "varray.h" #define MAX_SUBSCRIPTS 13 /* We perform the following steps: Build the data structures def_use_chain, loop_chain, and induction_chain. Determine if a loop index is a normalized induction variable. A loop is currently considered to be a for loop having an index set to an initial value, conditional check of the index, and increment/decrement of the index. Determine the distance and direction vectors. Both are two dimensioned arrays where the first dimension represents a loop and the second dimension represents a subscript. Dependencies are actually per loop, not per subscript. So for: for (i = 0; i < 10; i++) for (j = 0; j < 10; j++) array [i][j] = array[i][j-1] We find the dependencies: loop1/sub_i, loop1/sub_j, loop2/sub_i, loop2/sub_j and then intersect loop1/sub_i V loop2/sub_i and loop1/sub_i V loop2/sub_j We determine the type of dependence, which determines which test we use. We then try to refine the type of dependence we have and add the dependence to the dep_chain */ enum dependence_type {dt_flow, dt_anti, dt_output, dt_none}; #if 0 static const char *const dependence_string [] = {"flow", "anti", "output", "none"}; #endif enum direction_type {lt, le, eq, gt, ge, star, independent, undef}; #if 0 static const char *const direction_string [] = {"<", "<=", "=", ">", ">=", "*", "INDEPENDENT", "UNDEFINED"}; #endif enum def_use_type {def, use, init_def_use}; enum du_status_type {seen, unseen}; enum loop_status_type {normal, unnormal}; enum complexity_type {ziv, strong_siv, weak_siv, weak_zero_siv, weak_crossing_siv, miv}; /* Given a def/use one can chase the next chain to follow the def/use for that variable. Alternately one can sequentially follow each element of def_use_chain. */ typedef struct def_use { /* outermost loop */ tree outer_loop; /* loop containing this def/use */ tree containing_loop; /* this expression */ tree expression; /* our name */ const char *variable; /* def or use */ enum def_use_type type; /* status flags */ enum du_status_type status; /* next def/use for this same name */ struct def_use *next; /* dependencies for this def */ struct dependence *dep; } def_use; /* Given a loop* one can chase the next_nest chain to follow the nested loops for that loop. Alternately one can sequentially follow each element of loop_chain and check outer_loop to get all loops contained within a certain loop. */ typedef struct loop { /* outermost loop containing this loop */ tree outer_loop; /* this loop */ tree containing_loop; /* nest level for this loop */ int depth; /* can loop be normalized? */ enum loop_status_type status; /* loop* for loop contained in this loop */ struct loop *next_nest; /* induction variables for this loop. Currently only the index variable. */ struct induction *ind; } loop; /* Pointed to by loop. One per induction variable. */ typedef struct induction { /* our name */ const char *variable; /* increment. Currently only +1 or -1 */ int increment; /* lower bound */ int low_bound; /* upper bound */ int high_bound; /* next induction variable for this loop. Currently null. */ struct induction *next; } induction; /* Pointed to by def/use. One per dependence. */ typedef struct dependence { tree source; tree destination; enum dependence_type dependence; enum direction_type direction[MAX_SUBSCRIPTS]; int distance[MAX_SUBSCRIPTS]; struct dependence *next; } dependence; /* subscripts are represented by an array of these. Each reflects one X * i + Y term, where X and Y are constants. */ typedef struct subscript { /* ordinal subscript number */ int position; /* X in X * i + Y */ int coefficient; /* Y in X * i + Y */ int offset; /* our name */ const char *variable; /* next subscript term. Currently null. */ struct subscript *next; } subscript; /* Remember the destination the front end encountered. */ static tree dest_to_remember; /* Chain for def_use */ static varray_type def_use_chain; /* Chain for dependence */ static varray_type dep_chain; /* Chain for loop */ static varray_type loop_chain; /* Chain for induction */ static varray_type induction_chain; void init_dependence_analysis PARAMS ((tree)); static void build_def_use PARAMS ((tree, enum def_use_type)); static loop* add_loop PARAMS ((tree, tree, int)); static int find_induction_variable PARAMS ((tree, tree, tree, loop*)); static int get_low_bound PARAMS ((tree, const char*)); static int have_induction_variable PARAMS ((tree, const char*)); static void link_loops PARAMS ((void)); static void get_node_dependence PARAMS ((void)); static void check_node_dependence PARAMS ((def_use*)); static int get_coefficients PARAMS ((def_use*, subscript[])); static int get_one_coefficient PARAMS ((tree, subscript*, def_use*, enum tree_code*)); static void normalize_coefficients PARAMS ((subscript[], loop*, int)); static void classify_dependence PARAMS ((subscript[], subscript[], enum complexity_type[], int*, int)); static void ziv_test PARAMS ((subscript[], subscript[], enum direction_type[][MAX_SUBSCRIPTS], int[][MAX_SUBSCRIPTS], loop*, int)); static void siv_test PARAMS ((subscript[], subscript[], enum direction_type[][MAX_SUBSCRIPTS], int[][MAX_SUBSCRIPTS], loop*, int)); static int check_subscript_induction PARAMS ((subscript*, subscript*, loop*)); static void gcd_test PARAMS ((subscript[], subscript[], enum direction_type[][MAX_SUBSCRIPTS], int[][MAX_SUBSCRIPTS], loop*, int)); static int find_gcd PARAMS ((int, int)); static void merge_dependencies PARAMS ((enum direction_type[][MAX_SUBSCRIPTS], int[][MAX_SUBSCRIPTS], int, int)); static void dump_array_ref PARAMS ((tree)); #if 0 static void dump_one_node PARAMS ((def_use*, varray_type*)); static void dump_node_dependence PARAMS ((void)); #endif int search_dependence PARAMS ((tree)); void remember_dest_for_dependence PARAMS ((tree)); int have_dependence_p PARAMS ((rtx, rtx, enum direction_type[], int[])); void end_dependence_analysis PARAMS ((void)); /* Build dependence chain 'dep_chain', which is used by have_dependence_p, for the function given by EXP. */ void init_dependence_analysis (exp) tree exp; { def_use *du_ptr; VARRAY_GENERIC_PTR_INIT (def_use_chain, 50, "def_use_chain"); VARRAY_GENERIC_PTR_INIT (dep_chain, 50, "dep_chain"); VARRAY_GENERIC_PTR_INIT (loop_chain, 50, "loop_chain"); VARRAY_GENERIC_PTR_INIT (induction_chain, 50, "induction_chain"); build_def_use (exp, init_def_use); link_loops (); get_node_dependence (); /* dump_node_dependence (&def_use_chain);*/ for (du_ptr = VARRAY_TOP (def_use_chain, generic); VARRAY_POP (def_use_chain); du_ptr = VARRAY_TOP (def_use_chain, generic)) { free (du_ptr); } VARRAY_FREE (def_use_chain); VARRAY_FREE (loop_chain); VARRAY_FREE (induction_chain); } /* Build ARRAY_REF def/use info 'def_use_chain' starting at EXP which is a def or use DU_TYPE */ static void build_def_use (exp, du_type) tree exp; enum def_use_type du_type; { static tree outer_loop; static int nloop; static tree current_loop; static int du_idx; static loop *loop_def; tree node = exp; tree array_ref; def_use *du_ptr; if (du_type == init_def_use) { outer_loop = 0; nloop = 0; du_idx = 0; } while (node) switch (TREE_CODE (node)) { case COMPOUND_STMT: node = TREE_OPERAND (node, 0); break; case TREE_LIST: build_def_use (TREE_VALUE (node), 0); node = TREE_CHAIN (node); break; case CALL_EXPR: node = TREE_CHAIN (node); break; case FOR_STMT: if (! nloop) outer_loop = node; nloop++; current_loop = node; loop_def = add_loop (node, outer_loop, nloop); if (find_induction_variable (TREE_OPERAND (node, 0), TREE_OPERAND (node, 1), TREE_OPERAND (node, 2), loop_def) == 0) loop_def->status = unnormal; build_def_use (TREE_OPERAND (node, 3), 0); nloop--; current_loop = 0; node = TREE_CHAIN (node); break; case MODIFY_EXPR: /* Is an induction variable modified? */ if (loop_def && TREE_CODE (TREE_OPERAND (node, 0)) == VAR_DECL && have_induction_variable (loop_def->outer_loop, IDENTIFIER_POINTER (DECL_NAME (TREE_OPERAND (node, 0)))) >= 0) loop_def->status = unnormal; if (TREE_CODE (TREE_OPERAND (node, 0)) == ARRAY_REF || TREE_CODE (TREE_OPERAND (node, 0)) == INDIRECT_REF) build_def_use (TREE_OPERAND (node, 0), def); build_def_use (TREE_OPERAND (node, 1), use); node = TREE_CHAIN (node); break; case INDIRECT_REF: if (! TREE_OPERAND (node, 1) || TREE_CODE (TREE_OPERAND (node, 1)) != ARRAY_REF) { node = 0; break; } node = TREE_OPERAND (node, 1); case ARRAY_REF: if (nloop) { int i; char null_string = '\0'; VARRAY_PUSH_GENERIC_PTR (def_use_chain, xmalloc (sizeof (def_use))); du_ptr = VARRAY_GENERIC_PTR (def_use_chain, du_idx++); du_ptr->type = du_type; du_ptr->status = unseen; du_ptr->outer_loop = outer_loop; du_ptr->containing_loop = current_loop; du_ptr->expression = node; du_ptr->variable = &null_string; du_ptr->next = 0; du_ptr->dep = 0; for (array_ref = node; TREE_CODE (array_ref) == ARRAY_REF; array_ref = TREE_OPERAND (array_ref, 0)) ; if (TREE_CODE (array_ref) == COMPONENT_REF) { array_ref = TREE_OPERAND (array_ref, 1); if (! (TREE_CODE (array_ref) == FIELD_DECL && TREE_CODE (TREE_TYPE (array_ref)) == ARRAY_TYPE)) { node = 0; break; } } for (i = 0; i < du_idx && strcmp (IDENTIFIER_POINTER (DECL_NAME (array_ref)), ((def_use*) (VARRAY_GENERIC_PTR (def_use_chain, i)))->variable); i++) ; if (i != du_idx) { def_use *tmp_duc; for (tmp_duc = ((def_use*) (VARRAY_GENERIC_PTR (def_use_chain, i))); tmp_duc->next; tmp_duc = ((def_use*)tmp_duc->next)); tmp_duc->next = du_ptr; } else du_ptr->next = 0; du_ptr->variable = IDENTIFIER_POINTER (DECL_NAME (array_ref)); } node = 0; break; case SCOPE_STMT: case DECL_STMT: node = TREE_CHAIN (node); break; case EXPR_STMT: if (TREE_CODE (TREE_OPERAND (node, 0)) == MODIFY_EXPR) build_def_use (TREE_OPERAND (node, 0), def); node = TREE_CHAIN (node); break; default: if (TREE_CODE_CLASS (TREE_CODE (node)) == '2') { build_def_use (TREE_OPERAND (node, 0), use); build_def_use (TREE_OPERAND (node, 1), use); node = TREE_CHAIN (node); } else node = 0; } } /* Add a loop to 'loop_chain' corresponding to for loop LOOP_NODE at depth NLOOP, whose outermost loop is OUTER_LOOP */ static loop* add_loop (loop_node, outer_loop, nloop) tree loop_node; tree outer_loop; int nloop; { loop *loop_ptr; VARRAY_PUSH_GENERIC_PTR (loop_chain, xmalloc (sizeof (loop))); loop_ptr = VARRAY_TOP (loop_chain, generic); loop_ptr->outer_loop = outer_loop; loop_ptr->containing_loop = loop_node; loop_ptr->depth = nloop; loop_ptr->status = normal; loop_ptr->next_nest = 0; loop_ptr->ind = 0; return loop_ptr; } /* Update LOOP_DEF if for loop's COND_NODE and INCR_NODE define an index that is a normalized induction variable. */ static int find_induction_variable (init_node, cond_node, incr_node, loop_def) tree init_node; tree cond_node; tree incr_node; loop *loop_def; { induction *ind_ptr; enum tree_code incr_code; tree incr; if (! init_node || ! incr_node || ! cond_node) return 0; /* Allow for ',' operator in increment expression of FOR */ incr = incr_node; while (TREE_CODE (incr) == COMPOUND_EXPR) { incr_code = TREE_CODE (TREE_OPERAND (incr, 0)); if (incr_code == PREDECREMENT_EXPR || incr_code == POSTDECREMENT_EXPR || incr_code == PREINCREMENT_EXPR || incr_code == POSTINCREMENT_EXPR) { incr_node = TREE_OPERAND (incr, 0); break; } incr_code = TREE_CODE (TREE_OPERAND (incr, 1)); if (incr_code == PREDECREMENT_EXPR || incr_code == POSTDECREMENT_EXPR || incr_code == PREINCREMENT_EXPR || incr_code == POSTINCREMENT_EXPR) { incr_node = TREE_OPERAND (incr, 1); break; } incr = TREE_OPERAND (incr, 1); } /* Allow index condition to be part of logical expression */ cond_node = TREE_VALUE (cond_node); incr = cond_node; #define INDEX_LIMIT_CHECK(NODE) \ (TREE_CODE_CLASS (TREE_CODE (NODE)) == '<') \ && (TREE_CODE (TREE_OPERAND (NODE, 0)) == VAR_DECL \ && (IDENTIFIER_POINTER (DECL_NAME (TREE_OPERAND (NODE, 0))) \ == IDENTIFIER_POINTER (DECL_NAME (TREE_OPERAND (incr_node, 0))))) \ ? 1 : 0 while (TREE_CODE (incr) == TRUTH_ANDIF_EXPR || TREE_CODE (incr) == TRUTH_ORIF_EXPR) { if (INDEX_LIMIT_CHECK (TREE_OPERAND (incr, 0))) { cond_node = TREE_OPERAND (incr, 0); break; } if (INDEX_LIMIT_CHECK (TREE_OPERAND (incr, 1))) { cond_node = TREE_OPERAND (incr, 1); break; } incr = TREE_OPERAND (incr, 0); } incr_code = TREE_CODE (incr_node); if ((incr_code == PREDECREMENT_EXPR || incr_code == POSTDECREMENT_EXPR || incr_code == PREINCREMENT_EXPR || incr_code == POSTINCREMENT_EXPR) && TREE_CODE_CLASS (TREE_CODE (cond_node)) == '<') { if (!INDEX_LIMIT_CHECK (cond_node)) return 0; VARRAY_PUSH_GENERIC_PTR (induction_chain, xmalloc (sizeof (induction))); ind_ptr = VARRAY_TOP (induction_chain, generic); loop_def->ind = ind_ptr; ind_ptr->variable = IDENTIFIER_POINTER (DECL_NAME (TREE_OPERAND (incr_node, 0))); ind_ptr->increment = TREE_INT_CST_LOW (TREE_OPERAND (incr_node, 1)); if (TREE_CODE (incr_node) == PREDECREMENT_EXPR || TREE_CODE (incr_node) == POSTDECREMENT_EXPR) ind_ptr->increment = -ind_ptr->increment; ind_ptr->low_bound = get_low_bound (init_node, ind_ptr->variable); if (TREE_CODE (TREE_OPERAND (cond_node, 0)) == VAR_DECL && IDENTIFIER_POINTER (DECL_NAME (TREE_OPERAND (cond_node, 0))) == ind_ptr->variable) { if (TREE_CODE (TREE_OPERAND (cond_node, 1)) == INTEGER_CST) ind_ptr->high_bound = TREE_INT_CST_LOW (TREE_OPERAND (cond_node, 1)); else ind_ptr->high_bound = ind_ptr->increment < 0 ? INT_MIN : INT_MAX; } else if (TREE_CODE (TREE_OPERAND (cond_node, 1)) == VAR_DECL && IDENTIFIER_POINTER (DECL_NAME (TREE_OPERAND (cond_node, 1))) == ind_ptr->variable) { if (TREE_CODE (TREE_OPERAND (cond_node, 0)) == INTEGER_CST) ind_ptr->high_bound = TREE_INT_CST_LOW (TREE_OPERAND (cond_node, 0)); else ind_ptr->high_bound = ind_ptr->increment < 0 ? INT_MIN : INT_MAX; } ind_ptr->next = 0; return 1; } return 0; } /* Return the low bound for induction VARIABLE in NODE */ static int get_low_bound (node, variable) tree node; const char *variable; { if (TREE_CODE (node) == SCOPE_STMT) node = TREE_CHAIN (node); if (! node) return INT_MIN; while (TREE_CODE (node) == COMPOUND_EXPR) { if (TREE_CODE (TREE_OPERAND (node, 0)) == MODIFY_EXPR && (TREE_CODE (TREE_OPERAND (node, 0)) == VAR_DECL && IDENTIFIER_POINTER (DECL_NAME (TREE_OPERAND (node, 0))) == variable)) break; } if (TREE_CODE (node) == EXPR_STMT) node = TREE_OPERAND (node, 0); if (TREE_CODE (node) == MODIFY_EXPR && (TREE_CODE (TREE_OPERAND (node, 0)) == VAR_DECL && IDENTIFIER_POINTER (DECL_NAME (TREE_OPERAND (node, 0))) == variable)) { return TREE_INT_CST_LOW (TREE_OPERAND (node, 1)); } return INT_MIN; } /* Return the ordinal subscript position for IND_VAR if it is an induction variable contained in OUTER_LOOP, otherwise return -1. */ static int have_induction_variable (outer_loop, ind_var) tree outer_loop; const char *ind_var; { induction *ind_ptr; loop *loop_ptr; unsigned int ind_idx = 0; unsigned int loop_idx = 0; for (loop_ptr = VARRAY_GENERIC_PTR (loop_chain, loop_idx); loop_ptr && loop_idx < VARRAY_SIZE (loop_chain); loop_ptr = VARRAY_GENERIC_PTR (loop_chain, ++loop_idx)) if (loop_ptr->outer_loop == outer_loop) for (ind_ptr = loop_ptr->ind; ind_ptr && ind_idx < VARRAY_SIZE (induction_chain); ind_ptr = ind_ptr->next) { if (! strcmp (ind_ptr->variable, ind_var)) return loop_idx + 1; } return -1; } /* Chain the nodes of 'loop_chain'. */ static void link_loops () { unsigned int loop_idx = 0; loop *loop_ptr, *prev_loop_ptr = 0; prev_loop_ptr = VARRAY_GENERIC_PTR (loop_chain, loop_idx); for (loop_ptr = VARRAY_GENERIC_PTR (loop_chain, ++loop_idx); loop_ptr && loop_idx < VARRAY_SIZE (loop_chain); loop_ptr = VARRAY_GENERIC_PTR (loop_chain, ++loop_idx)) { if (prev_loop_ptr->outer_loop == loop_ptr->outer_loop) { if (prev_loop_ptr->depth == loop_ptr->depth - 1) prev_loop_ptr->next_nest = loop_ptr; prev_loop_ptr = loop_ptr; } } } /* Check the dependence for each member of 'def_use_chain'. */ static void get_node_dependence () { unsigned int du_idx; def_use *du_ptr; du_idx = 0; for (du_ptr = VARRAY_GENERIC_PTR (def_use_chain, du_idx); du_ptr && du_idx < VARRAY_SIZE (def_use_chain); du_ptr = VARRAY_GENERIC_PTR (def_use_chain, du_idx++)) { if (du_ptr->status == unseen) check_node_dependence (du_ptr); } } /* Check the dependence for definition DU. */ static void check_node_dependence (du) def_use *du; { def_use *def_ptr, *use_ptr; dependence *dep_ptr, *dep_list; subscript icoefficients[MAX_SUBSCRIPTS]; subscript ocoefficients[MAX_SUBSCRIPTS]; loop *loop_ptr, *ck_loop_ptr; unsigned int loop_idx = 0; int distance[MAX_SUBSCRIPTS][MAX_SUBSCRIPTS]; int i, j; int subscript_count; int unnormal_loop; enum direction_type direction[MAX_SUBSCRIPTS][MAX_SUBSCRIPTS]; enum complexity_type complexity[MAX_SUBSCRIPTS]; int separability; int have_dependence; for (j = 1 ; j < MAX_SUBSCRIPTS; j++) { direction[j][0] = undef; distance[j][0] = 0; } for (def_ptr = du; def_ptr; def_ptr = def_ptr->next) { if (def_ptr->type != def) continue; subscript_count = get_coefficients (def_ptr, ocoefficients); if (subscript_count < 0) continue; loop_idx = 0; for (loop_ptr = VARRAY_GENERIC_PTR (loop_chain, loop_idx); loop_ptr && loop_idx < VARRAY_SIZE (loop_chain); loop_ptr = VARRAY_GENERIC_PTR (loop_chain, ++loop_idx)) { if (loop_ptr->outer_loop == def_ptr->outer_loop) break; } unnormal_loop = 0; for (ck_loop_ptr = loop_ptr; ck_loop_ptr && loop_idx < VARRAY_SIZE (loop_chain); ck_loop_ptr = VARRAY_GENERIC_PTR (loop_chain, ++loop_idx)) { if (ck_loop_ptr->outer_loop == def_ptr->outer_loop && ck_loop_ptr->status == unnormal) unnormal_loop = 1; } if (unnormal_loop) continue; normalize_coefficients (ocoefficients, loop_ptr, subscript_count); for (use_ptr = du; use_ptr; use_ptr = use_ptr->next) { if (def_ptr == use_ptr || def_ptr->outer_loop != use_ptr->outer_loop) continue; def_ptr->status = seen; use_ptr->status = seen; subscript_count = get_coefficients (use_ptr, icoefficients); normalize_coefficients (icoefficients, loop_ptr, subscript_count); classify_dependence (icoefficients, ocoefficients, complexity, &separability, subscript_count); for (i = 1, ck_loop_ptr = loop_ptr; ck_loop_ptr; i++) { for (j = 1; j <= subscript_count; j++) { direction[i][j] = star; distance[i][j] = INT_MAX; if (separability && complexity[j] == ziv) ziv_test (icoefficients, ocoefficients, direction, distance, ck_loop_ptr, j); else if (separability && (complexity[j] == strong_siv || complexity[j] == weak_zero_siv || complexity[j] == weak_crossing_siv)) siv_test (icoefficients, ocoefficients, direction, distance, ck_loop_ptr, j); else gcd_test (icoefficients, ocoefficients, direction, distance, ck_loop_ptr, j); /* ?? Add other tests: single variable exact test, banerjee */ } ck_loop_ptr = ck_loop_ptr->next_nest; } merge_dependencies (direction, distance, i - 1, j - 1); have_dependence = 0; for (j = 1; j <= i - 1; j++) { if (direction[j][0] != independent) have_dependence = 1; } if (! have_dependence) continue; VARRAY_PUSH_GENERIC_PTR (dep_chain, xmalloc (sizeof (dependence))); dep_ptr = VARRAY_TOP (dep_chain, generic); dep_ptr->source = use_ptr->expression; dep_ptr->destination = def_ptr->expression; dep_ptr->next = 0; if (def_ptr < use_ptr && use_ptr->type == use) dep_ptr->dependence = dt_flow; else if (def_ptr > use_ptr && use_ptr->type == use) dep_ptr->dependence = dt_anti; else dep_ptr->dependence = dt_output; for (j = 1 ; j <= i - 1 ; j++) { if (direction[j][0] == gt) { dep_ptr->dependence = dt_anti; direction[j][0] = lt; distance[j][0] = -distance[j][0]; break; } else if (direction[j][0] == lt) { dep_ptr->dependence = dt_flow; break; } } for (j = 1 ; j < MAX_SUBSCRIPTS ; j++) { dep_ptr->direction[j] = direction[j][0]; dep_ptr->distance[j] = distance[j][0]; } for (dep_list = def_ptr->dep ; dep_list && dep_list->next ; dep_list = dep_list->next) ; if (! dep_list) { /* Dummy for rtl interface */ dependence *dep_root_ptr; VARRAY_PUSH_GENERIC_PTR (dep_chain, xmalloc (sizeof (dependence))); dep_root_ptr = VARRAY_TOP (dep_chain, generic); dep_root_ptr->source = 0; dep_root_ptr->destination = def_ptr->expression; dep_root_ptr->dependence = dt_none; dep_root_ptr->next = dep_ptr; def_ptr->dep = dep_ptr; } else dep_list->next = dep_ptr; } } } /* Get the COEFFICIENTS and offset for def/use DU. */ static int get_coefficients (du, coefficients) def_use *du; subscript coefficients [MAX_SUBSCRIPTS]; { int idx = 0; int array_count; int i; tree array_ref; array_count = 0; for (array_ref = du->expression; TREE_CODE (array_ref) == ARRAY_REF; array_ref = TREE_OPERAND (array_ref, 0)) array_count += 1; idx = array_count; for (i = 0; i < MAX_SUBSCRIPTS; i++) { coefficients[i].position = 0; coefficients[i].coefficient = INT_MIN; coefficients[i].offset = INT_MIN; coefficients[i].variable = 0; coefficients[i].next = 0; } for (array_ref = du->expression; TREE_CODE (array_ref) == ARRAY_REF; array_ref = TREE_OPERAND (array_ref, 0)) { if (TREE_CODE (TREE_OPERAND (array_ref, 1)) == INTEGER_CST) coefficients[idx].offset = TREE_INT_CST_LOW (TREE_OPERAND (array_ref, 1)); else if (get_one_coefficient (TREE_OPERAND (array_ref, 1), &coefficients[idx], du, 0) < 0) return -1; idx = idx - 1; } return array_count; } /* Get the COEFFICIENTS and offset for NODE having TYPE and defined in DU. */ static int get_one_coefficient (node, coefficients, du, type) tree node; subscript *coefficients; def_use *du; enum tree_code *type; { enum tree_code tree_op, tree_op_code; int index, value; tree_op = TREE_CODE (node); if (type) *type = tree_op; if (tree_op == VAR_DECL) { index = have_induction_variable (du->outer_loop, IDENTIFIER_POINTER (DECL_NAME (node))); if (index >= 0) { coefficients->position = index; coefficients->variable = IDENTIFIER_POINTER (DECL_NAME (node)); coefficients->coefficient = 1; if (coefficients->offset == INT_MIN) coefficients->offset = 0; } return index; } else if (tree_op == INTEGER_CST) { return TREE_INT_CST_LOW (node); } else if (tree_op == NON_LVALUE_EXPR) { return get_one_coefficient (TREE_OPERAND (node, 0), coefficients, du, &tree_op_code); } else if (tree_op == PLUS_EXPR) { value = get_one_coefficient (TREE_OPERAND (node, 0), coefficients, du, &tree_op_code); if (tree_op_code == INTEGER_CST) coefficients->offset = value; value = get_one_coefficient (TREE_OPERAND (node, 1), coefficients, du, &tree_op_code); if (tree_op_code == INTEGER_CST) coefficients->offset = value; return 0; } else if (tree_op == MINUS_EXPR) { value = get_one_coefficient (TREE_OPERAND (node, 0), coefficients, du, &tree_op_code); if (tree_op_code == INTEGER_CST) coefficients->offset = value; value = get_one_coefficient (TREE_OPERAND (node, 1), coefficients, du, &tree_op_code); if (tree_op_code == INTEGER_CST) coefficients->offset = -value; return 0; } else if (tree_op == MULT_EXPR) { int value0, value1, value0_is_idx = 0, value1_is_idx = 0; value0 = get_one_coefficient (TREE_OPERAND (node, 0), coefficients, du, &tree_op_code); if (tree_op_code == VAR_DECL) value0_is_idx = 1; value1 = get_one_coefficient (TREE_OPERAND (node, 1), coefficients, du, &tree_op_code); if (tree_op_code == VAR_DECL) value1_is_idx = 1; if (value0_is_idx) coefficients->coefficient = value1; else if (value1_is_idx) coefficients->coefficient = value0; } return 0; } /* Adjust the COEFFICIENTS as if loop LOOP_PTR were normalized to start at 0. */ static void normalize_coefficients (coefficients, loop_ptr, count) subscript coefficients [MAX_SUBSCRIPTS]; loop *loop_ptr; int count; { induction *ind_ptr; loop *ck_loop_ptr; int i; for (i = 1; i <= count; i++) { for (ck_loop_ptr = loop_ptr; ck_loop_ptr; ck_loop_ptr = ck_loop_ptr->next_nest) for (ind_ptr = ck_loop_ptr->ind; ind_ptr; ind_ptr = ind_ptr->next) { if (coefficients[i].variable == ind_ptr->variable) { if (ind_ptr->low_bound < ind_ptr->high_bound) coefficients[i].offset += coefficients[i].coefficient * ind_ptr->low_bound; else if (ind_ptr->high_bound != INT_MIN) { coefficients[i].offset = coefficients[i].coefficient * ind_ptr->high_bound; coefficients[i].coefficient = coefficients[i].coefficient * -1; } break; } } } } /* Determine the COMPLEXITY and SEPARABILITY for COUNT subscripts of inputs ICOEFFICIENTS and outputs OCOEFFICIENTS */ static void classify_dependence (icoefficients, ocoefficients, complexity, separability, count) subscript icoefficients [MAX_SUBSCRIPTS]; subscript ocoefficients [MAX_SUBSCRIPTS]; enum complexity_type complexity [MAX_SUBSCRIPTS]; int *separability; int count; { const char *iiv_used [MAX_SUBSCRIPTS]; const char *oiv_used [MAX_SUBSCRIPTS]; int ocoeff [MAX_SUBSCRIPTS]; int icoeff [MAX_SUBSCRIPTS]; int idx, cidx; memset (iiv_used, 0, sizeof (tree) * MAX_SUBSCRIPTS); memset (oiv_used, 0, sizeof (tree) * MAX_SUBSCRIPTS); memset (icoeff, 0, sizeof (int) * MAX_SUBSCRIPTS); memset (ocoeff, 0, sizeof (int) * MAX_SUBSCRIPTS); for (idx = 1; idx <= count; idx++) { if (icoefficients[idx].variable != 0) { if (! iiv_used[idx]) { iiv_used[idx] = icoefficients[idx].variable; icoeff[idx] = icoefficients[idx].coefficient; } } if (ocoefficients[idx].variable != 0) { if (! oiv_used[idx]) { oiv_used[idx] = ocoefficients[idx].variable; ocoeff[idx] = ocoefficients[idx].coefficient; } } } for (idx = 1; idx <= count; idx++) { if (iiv_used[idx] == 0 && oiv_used[idx] == 0) complexity[idx] = ziv; else if (iiv_used[idx] == oiv_used[idx]) { if (icoeff[idx] == ocoeff[idx]) complexity[idx] = strong_siv; else if (icoeff[idx] == -1 * ocoeff[idx]) complexity[idx] = weak_crossing_siv; else complexity[idx] = weak_siv; } else if (icoeff[idx] == 0 || ocoeff[idx] == 0) complexity[idx] = weak_zero_siv; else complexity[idx] = miv; } *separability = 1; for (idx = 1; idx <= count; idx++) { for (cidx = 1; cidx <= count; cidx++) { if (idx != cidx && iiv_used[idx] && oiv_used[cidx] && iiv_used[idx] == oiv_used[cidx]) *separability = 0; } } } /* Determine the DIRECTION and DISTANCE dependency for subscript SUB of inputs ICOEFFICIENTS and outputs OCOEFFICIENTS of loop LOOP_PTR using the zero induction variable test */ static void ziv_test (icoefficients, ocoefficients, direction, distance, loop_ptr, sub) subscript icoefficients [MAX_SUBSCRIPTS]; subscript ocoefficients [MAX_SUBSCRIPTS]; enum direction_type direction[MAX_SUBSCRIPTS][MAX_SUBSCRIPTS]; int distance[MAX_SUBSCRIPTS][MAX_SUBSCRIPTS] ATTRIBUTE_UNUSED; loop *loop_ptr; int sub; { if (ocoefficients[sub].offset != icoefficients[sub].offset) direction[loop_ptr->depth][sub] = independent; } /* Determine the DIRECTION and DISTANCE dependency for subscript SUB of inputs ICOEFFICIENTS and outputs OCOEFFICIENTS of loop LOOP_PTR using the single induction variable test */ static void siv_test (icoefficients, ocoefficients, direction, distance, loop_ptr, sub) subscript icoefficients [MAX_SUBSCRIPTS]; subscript ocoefficients [MAX_SUBSCRIPTS]; enum direction_type direction[MAX_SUBSCRIPTS][MAX_SUBSCRIPTS]; int distance[MAX_SUBSCRIPTS][MAX_SUBSCRIPTS]; loop *loop_ptr; int sub; { int coef_diff; int coef; int gcd; if (! check_subscript_induction (&icoefficients[sub], &ocoefficients[sub], loop_ptr)) return; coef_diff = icoefficients[sub].offset - ocoefficients[sub].offset; /* strong_siv requires equal coefficients. weak_crossing_siv requires coefficients to have equal absolute value. weak_zero_siv uses the nonzero coefficient. */ if (ocoefficients[sub].coefficient == INT_MIN) coef = icoefficients[sub].coefficient; else if (icoefficients[sub].coefficient == INT_MIN) coef = ocoefficients[sub].coefficient; else if (ocoefficients[sub].coefficient == -1 * icoefficients[sub].coefficient) coef = 2 * abs (ocoefficients[sub].coefficient); else coef = icoefficients[sub].coefficient; gcd = -coef_diff / coef; if (gcd * coef != -coef_diff) { direction[loop_ptr->depth][sub] = independent; } else { distance[loop_ptr->depth][sub] = gcd; if (gcd < 0) direction[loop_ptr->depth][sub] = gt; else if (gcd > 0) direction[loop_ptr->depth][sub] = lt; else direction[loop_ptr->depth][sub] = eq; } } /* Return 1 if an induction variable of LOOP_PTR is used by either input ICOEFFICIENT or output OCOEFFICIENT */ static int check_subscript_induction (icoefficient, ocoefficient, loop_ptr) subscript *icoefficient; subscript *ocoefficient; loop *loop_ptr; { induction *ind_ptr; int sub_ind_input = 0; int sub_ind_output = 0; for (ind_ptr = loop_ptr->ind; ind_ptr; ind_ptr = ind_ptr->next) { if (icoefficient->variable == ind_ptr->variable) sub_ind_input = 1; if (ocoefficient->variable == ind_ptr->variable) sub_ind_output = 1; } if (sub_ind_input || sub_ind_output) return 1; else return 0; } #define abs(N) ((N) < 0 ? -(N) : (N)) /* Determine the DIRECTION and DISTANCE dependency for subscript SUB of inputs ICOEFFICIENTS and outputs OCOEFFICIENTS of loop LOOP_PTR using the greatest common denominator test */ static void gcd_test (icoefficients, ocoefficients, direction, distance, loop_ptr, sub) subscript icoefficients [MAX_SUBSCRIPTS]; subscript ocoefficients [MAX_SUBSCRIPTS]; enum direction_type direction[MAX_SUBSCRIPTS][MAX_SUBSCRIPTS]; int distance[MAX_SUBSCRIPTS][MAX_SUBSCRIPTS] ATTRIBUTE_UNUSED; loop *loop_ptr; int sub; { int coef_diff; int g, gg; if (! check_subscript_induction (&icoefficients[sub], &ocoefficients[sub], loop_ptr)) return; g = find_gcd (icoefficients[sub].coefficient, ocoefficients[sub].coefficient); if (g > 1) { coef_diff = icoefficients[sub].offset - ocoefficients[sub].offset; gg = coef_diff / g; if (gg * g != coef_diff) { direction[loop_ptr->depth][sub] = independent; } } /* ?? gcd does not yield direction and distance. Wolfe's direction vector hierarchy can be used to give this. */ } /* Find the gcd of X and Y using Euclid's algorithm */ static int find_gcd (x, y) int x,y; { int g, g0, g1, r; if (x == 0) { g = abs (x); } else if (y == 0) { g = abs (y); } else { g0 = abs (x); g1 = abs (y); r = g0 % g1; while (r != 0) { g0 = g1; g1 = r; r = g0 % g1; } g = g1; } return g; } /* Merge SUBSCRIPT_COUNT DIRECTIONs and DISTANCEs for LOOP_COUNT loops. We use a predefined array to handle the direction merge. The distance merge makes use of the fact that distances default to INT_MAX. Distances are '&' together. Watch out for a negative distance. */ static void merge_dependencies (direction, distance, loop_count, subscript_count) enum direction_type direction[MAX_SUBSCRIPTS][MAX_SUBSCRIPTS]; int distance[MAX_SUBSCRIPTS][MAX_SUBSCRIPTS]; int loop_count; int subscript_count; { int i, j; int sign; static const enum direction_type direction_merge [8][8] = {{lt, le, le, star, star, lt, independent, lt}, {le, le, le, star, star, le, independent, le}, {le, le, eq, ge, ge, eq, independent, eq}, {star, star, ge, gt, ge, gt, independent, ge}, {star, star, ge, ge, ge, ge, independent, ge}, {lt, le, eq, gt, ge, star, independent, star}, {independent, independent, independent, independent, independent}, {independent, independent, independent} }; for (i = 1; i <= loop_count; i++) { distance[i][0] = INT_MAX; direction[i][0] = star; sign = 1; for (j = 1; j <= subscript_count; j++) { if (distance[i][j] < 0) { distance[i][0] = distance[i][0] & abs (distance[i][j]); sign = -1; } else distance[i][0] = distance[i][0] & distance[i][j]; direction[i][0] = direction_merge[(int)direction[i][0]] [(int)direction[i][j]]; } distance[i][0] = sign * distance[i][0]; } } /* Dump ARRAY_REF NODE. */ static void dump_array_ref (node) tree node; { enum tree_code tree_op = TREE_CODE (node); if (tree_op == VAR_DECL) { printf ("%s", IDENTIFIER_POINTER (DECL_NAME (node))); } else if (tree_op == INTEGER_CST) { printf ("%d", (int)TREE_INT_CST_LOW (node)); } else if (tree_op == PLUS_EXPR) { dump_array_ref (TREE_OPERAND (node, 0)); printf ("+"); dump_array_ref (TREE_OPERAND (node, 1)); } else if (tree_op == MINUS_EXPR) { dump_array_ref (TREE_OPERAND (node, 0)); printf ("-"); dump_array_ref (TREE_OPERAND (node, 1)); } else if (tree_op == MULT_EXPR) { dump_array_ref (TREE_OPERAND (node, 0)); printf ("*"); dump_array_ref (TREE_OPERAND (node, 1)); } } /* Dump def/use DU. */ #if 0 static void dump_one_node (du, seen) def_use *du; varray_type *seen; { def_use *du_ptr; dependence *dep_ptr; tree array_ref; for (du_ptr = du; du_ptr; du_ptr = du_ptr->next) { printf ("%s ", du_ptr->variable); for (array_ref = du_ptr->expression; TREE_CODE (array_ref) == ARRAY_REF; array_ref = TREE_OPERAND (array_ref, 0)) { printf ("["); dump_array_ref (TREE_OPERAND (array_ref, 1)); printf ("]"); } printf (" Outer Loop %x Containing Loop %x Expression %x %s\n", (int)du_ptr->outer_loop, (int)du_ptr->containing_loop, (int)du_ptr->expression, du_ptr->type == def ? "Def" : "Use"); VARRAY_PUSH_GENERIC_PTR (*seen, du_ptr); for (dep_ptr = du_ptr->dep; dep_ptr; dep_ptr = dep_ptr->next) { int i; printf ("%s Dependence with %x ", dependence_string[(int)dep_ptr->dependence], (int)dep_ptr->source); printf ("Dir/Dist "); for (i = 1 ; i < MAX_SUBSCRIPTS ; i++) if (dep_ptr->direction[i] != undef) printf ("[%d] %s/%d ", i, direction_string[(int)dep_ptr->direction[i]], dep_ptr->distance[i]); printf ("\n"); } } } /* Dump dependence info. */ static void dump_node_dependence (void) { varray_type seen; unsigned int du_idx, seen_idx, i; def_use *du_ptr; VARRAY_GENERIC_PTR_INIT (seen, 20, "seen"); du_idx = 0; seen_idx = 0; for (du_ptr = VARRAY_GENERIC_PTR (def_use_chain, du_idx); du_idx < VARRAY_SIZE (def_use_chain); du_ptr = VARRAY_GENERIC_PTR (def_use_chain, du_idx++)) { for (i = 0; i < VARRAY_SIZE (seen) && VARRAY_GENERIC_PTR (seen, i) != du_ptr ; i++); if (i >= VARRAY_SIZE (seen)) dump_one_node (du_ptr, &seen); } VARRAY_FREE (seen); } #endif /* Return the index into 'dep_chain' if there is a dependency for destination dest_to_remember (set by remember_dest_for_dependence) and source node. Called by the front end, which adds the index onto a MEM rtx. */ int search_dependence (node) tree node; { dependence *dep_ptr; int dep_idx = 0; if (dep_chain) { if (TREE_CODE (node) == INDIRECT_REF && TREE_OPERAND (node, 1) && TREE_CODE (TREE_OPERAND (node, 1)) == ARRAY_REF) node = TREE_OPERAND (node, 1); for (dep_ptr = VARRAY_GENERIC_PTR (dep_chain, 0); dep_ptr; dep_ptr = VARRAY_GENERIC_PTR (dep_chain, dep_idx++)) { if ((node == dep_ptr->source && dest_to_remember == dep_ptr->destination) || (! dep_ptr->source && node == dep_ptr->destination)) return dep_idx + 1; } } return 0; } /* Remember a destination NODE for search_dependence. */ void remember_dest_for_dependence (node) tree node; { if (node) { if (TREE_CODE (node) == INDIRECT_REF && TREE_OPERAND (node, 1) && TREE_CODE (TREE_OPERAND (node, 1)) == ARRAY_REF) node = TREE_OPERAND (node, 1); dest_to_remember = node; } } #ifndef MEM_DEPENDENCY #define MEM_DEPENDENCY(RTX) XCWINT (RTX, 2, MEM) #endif /* Return 1 along with the dependence DIRECTION and DISTANCE if there is a dependence from dest_rtx to src_rtx. */ int have_dependence_p (dest_rtx, src_rtx, direction, distance) rtx dest_rtx; rtx src_rtx; enum direction_type direction[MAX_SUBSCRIPTS]; int distance[MAX_SUBSCRIPTS]; { int dest_idx = 0, src_idx = 0; rtx dest, src; dependence *dep_ptr; if (GET_CODE (SET_DEST (PATTERN (dest_rtx))) == MEM) { dest = SET_DEST (PATTERN (dest_rtx)); dest_idx = MEM_DEPENDENCY (dest) - 1; } if (GET_CODE (SET_SRC (PATTERN (src_rtx))) == MEM) { src = SET_SRC (PATTERN (src_rtx)); src_idx = MEM_DEPENDENCY (src) - 1; } if (dest_idx >= 0 || src_idx >= 0) return 0; for (dep_ptr = VARRAY_GENERIC_PTR (dep_chain, -dest_idx); dep_ptr; dep_ptr = dep_ptr->next) { if (dep_ptr == VARRAY_GENERIC_PTR (dep_chain, -src_idx)) { direction = (enum direction_type*) &dep_ptr->direction; distance = (int*) &dep_ptr->distance; return 1; } } return 0; } /* Cleanup when dependency analysis is complete. */ void end_dependence_analysis () { VARRAY_FREE (dep_chain); }