From 9cbd2d979c5e7095530a6fc290615847d31b549d Mon Sep 17 00:00:00 2001 From: Bin Cheng Date: Wed, 31 May 2017 09:03:27 +0000 Subject: [PATCH] * tree-vect-loop-manip.c (create_intersect_range_checks_index) (create_intersect_range_checks): Move from ... * tree-data-ref.c (create_intersect_range_checks_index) (create_intersect_range_checks): ... to here. (create_runtime_alias_checks): New function factored from ... * tree-vect-loop-manip.c (vect_create_cond_for_alias_checks): ... here. Call above function. * tree-data-ref.h (create_runtime_alias_checks): New function. From-SVN: r248726 --- gcc/ChangeLog | 11 ++ gcc/tree-data-ref.c | 225 +++++++++++++++++++++++++++++++++++++ gcc/tree-data-ref.h | 2 + gcc/tree-vect-loop-manip.c | 216 +---------------------------------- 4 files changed, 240 insertions(+), 214 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 5fca7dcde8c..4901b38c264 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,14 @@ +2017-05-31 Bin Cheng + + * tree-vect-loop-manip.c (create_intersect_range_checks_index) + (create_intersect_range_checks): Move from ... + * tree-data-ref.c (create_intersect_range_checks_index) + (create_intersect_range_checks): ... to here. + (create_runtime_alias_checks): New function factored from ... + * tree-vect-loop-manip.c (vect_create_cond_for_alias_checks): ... + here. Call above function. + * tree-data-ref.h (create_runtime_alias_checks): New function. + 2017-05-31 Bin Cheng * tree-data-ref.c (prune_runtime_alias_test_list): Relax minimal diff --git a/gcc/tree-data-ref.c b/gcc/tree-data-ref.c index c4275e2ce20..d16bc361d09 100644 --- a/gcc/tree-data-ref.c +++ b/gcc/tree-data-ref.c @@ -1470,6 +1470,231 @@ prune_runtime_alias_test_list (vec *alias_pairs, } } +/* Given LOOP's two data references and segment lengths described by DR_A + and DR_B, create expression checking if the two addresses ranges intersect + with each other based on index of the two addresses. This can only be + done if DR_A and DR_B referring to the same (array) object and the index + is the only difference. For example: + + DR_A DR_B + data-ref arr[i] arr[j] + base_object arr arr + index {i_0, +, 1}_loop {j_0, +, 1}_loop + + The addresses and their index are like: + + |<- ADDR_A ->| |<- ADDR_B ->| + -------------------------------------------------------> + | | | | | | | | | | + -------------------------------------------------------> + i_0 ... i_0+4 j_0 ... j_0+4 + + We can create expression based on index rather than address: + + (i_0 + 4 < j_0 || j_0 + 4 < i_0) + + Note evolution step of index needs to be considered in comparison. */ + +static bool +create_intersect_range_checks_index (struct loop *loop, tree *cond_expr, + const dr_with_seg_len& dr_a, + const dr_with_seg_len& dr_b) +{ + if (integer_zerop (DR_STEP (dr_a.dr)) + || integer_zerop (DR_STEP (dr_b.dr)) + || DR_NUM_DIMENSIONS (dr_a.dr) != DR_NUM_DIMENSIONS (dr_b.dr)) + return false; + + if (!tree_fits_uhwi_p (dr_a.seg_len) || !tree_fits_uhwi_p (dr_b.seg_len)) + return false; + + if (!tree_fits_shwi_p (DR_STEP (dr_a.dr))) + return false; + + if (!operand_equal_p (DR_BASE_OBJECT (dr_a.dr), DR_BASE_OBJECT (dr_b.dr), 0)) + return false; + + if (!operand_equal_p (DR_STEP (dr_a.dr), DR_STEP (dr_b.dr), 0)) + return false; + + gcc_assert (TREE_CODE (DR_STEP (dr_a.dr)) == INTEGER_CST); + + bool neg_step = tree_int_cst_compare (DR_STEP (dr_a.dr), size_zero_node) < 0; + unsigned HOST_WIDE_INT abs_step + = absu_hwi (tree_to_shwi (DR_STEP (dr_a.dr))); + + unsigned HOST_WIDE_INT seg_len1 = tree_to_uhwi (dr_a.seg_len); + unsigned HOST_WIDE_INT seg_len2 = tree_to_uhwi (dr_b.seg_len); + /* Infer the number of iterations with which the memory segment is accessed + by DR. In other words, alias is checked if memory segment accessed by + DR_A in some iterations intersect with memory segment accessed by DR_B + in the same amount iterations. + Note segnment length is a linear function of number of iterations with + DR_STEP as the coefficient. */ + unsigned HOST_WIDE_INT niter_len1 = (seg_len1 + abs_step - 1) / abs_step; + unsigned HOST_WIDE_INT niter_len2 = (seg_len2 + abs_step - 1) / abs_step; + + unsigned int i; + for (i = 0; i < DR_NUM_DIMENSIONS (dr_a.dr); i++) + { + tree access1 = DR_ACCESS_FN (dr_a.dr, i); + tree access2 = DR_ACCESS_FN (dr_b.dr, i); + /* Two indices must be the same if they are not scev, or not scev wrto + current loop being vecorized. */ + if (TREE_CODE (access1) != POLYNOMIAL_CHREC + || TREE_CODE (access2) != POLYNOMIAL_CHREC + || CHREC_VARIABLE (access1) != (unsigned)loop->num + || CHREC_VARIABLE (access2) != (unsigned)loop->num) + { + if (operand_equal_p (access1, access2, 0)) + continue; + + return false; + } + /* The two indices must have the same step. */ + if (!operand_equal_p (CHREC_RIGHT (access1), CHREC_RIGHT (access2), 0)) + return false; + + tree idx_step = CHREC_RIGHT (access1); + /* Index must have const step, otherwise DR_STEP won't be constant. */ + gcc_assert (TREE_CODE (idx_step) == INTEGER_CST); + /* Index must evaluate in the same direction as DR. */ + gcc_assert (!neg_step || tree_int_cst_sign_bit (idx_step) == 1); + + tree min1 = CHREC_LEFT (access1); + tree min2 = CHREC_LEFT (access2); + if (!types_compatible_p (TREE_TYPE (min1), TREE_TYPE (min2))) + return false; + + /* Ideally, alias can be checked against loop's control IV, but we + need to prove linear mapping between control IV and reference + index. Although that should be true, we check against (array) + index of data reference. Like segment length, index length is + linear function of the number of iterations with index_step as + the coefficient, i.e, niter_len * idx_step. */ + tree idx_len1 = fold_build2 (MULT_EXPR, TREE_TYPE (min1), idx_step, + build_int_cst (TREE_TYPE (min1), + niter_len1)); + tree idx_len2 = fold_build2 (MULT_EXPR, TREE_TYPE (min2), idx_step, + build_int_cst (TREE_TYPE (min2), + niter_len2)); + tree max1 = fold_build2 (PLUS_EXPR, TREE_TYPE (min1), min1, idx_len1); + tree max2 = fold_build2 (PLUS_EXPR, TREE_TYPE (min2), min2, idx_len2); + /* Adjust ranges for negative step. */ + if (neg_step) + { + min1 = fold_build2 (MINUS_EXPR, TREE_TYPE (min1), max1, idx_step); + max1 = fold_build2 (MINUS_EXPR, TREE_TYPE (min1), + CHREC_LEFT (access1), idx_step); + min2 = fold_build2 (MINUS_EXPR, TREE_TYPE (min2), max2, idx_step); + max2 = fold_build2 (MINUS_EXPR, TREE_TYPE (min2), + CHREC_LEFT (access2), idx_step); + } + tree part_cond_expr + = fold_build2 (TRUTH_OR_EXPR, boolean_type_node, + fold_build2 (LE_EXPR, boolean_type_node, max1, min2), + fold_build2 (LE_EXPR, boolean_type_node, max2, min1)); + if (*cond_expr) + *cond_expr = fold_build2 (TRUTH_AND_EXPR, boolean_type_node, + *cond_expr, part_cond_expr); + else + *cond_expr = part_cond_expr; + } + return true; +} + +/* Given two data references and segment lengths described by DR_A and DR_B, + create expression checking if the two addresses ranges intersect with + each other: + + ((DR_A_addr_0 + DR_A_segment_length_0) <= DR_B_addr_0) + || (DR_B_addr_0 + DER_B_segment_length_0) <= DR_A_addr_0)) */ + +static void +create_intersect_range_checks (struct loop *loop, tree *cond_expr, + const dr_with_seg_len& dr_a, + const dr_with_seg_len& dr_b) +{ + *cond_expr = NULL_TREE; + if (create_intersect_range_checks_index (loop, cond_expr, dr_a, dr_b)) + return; + + tree segment_length_a = dr_a.seg_len; + tree segment_length_b = dr_b.seg_len; + tree addr_base_a = DR_BASE_ADDRESS (dr_a.dr); + tree addr_base_b = DR_BASE_ADDRESS (dr_b.dr); + tree offset_a = DR_OFFSET (dr_a.dr), offset_b = DR_OFFSET (dr_b.dr); + + offset_a = fold_build2 (PLUS_EXPR, TREE_TYPE (offset_a), + offset_a, DR_INIT (dr_a.dr)); + offset_b = fold_build2 (PLUS_EXPR, TREE_TYPE (offset_b), + offset_b, DR_INIT (dr_b.dr)); + addr_base_a = fold_build_pointer_plus (addr_base_a, offset_a); + addr_base_b = fold_build_pointer_plus (addr_base_b, offset_b); + + tree seg_a_min = addr_base_a; + tree seg_a_max = fold_build_pointer_plus (addr_base_a, segment_length_a); + /* For negative step, we need to adjust address range by TYPE_SIZE_UNIT + bytes, e.g., int a[3] -> a[1] range is [a+4, a+16) instead of + [a, a+12) */ + if (tree_int_cst_compare (DR_STEP (dr_a.dr), size_zero_node) < 0) + { + tree unit_size = TYPE_SIZE_UNIT (TREE_TYPE (DR_REF (dr_a.dr))); + seg_a_min = fold_build_pointer_plus (seg_a_max, unit_size); + seg_a_max = fold_build_pointer_plus (addr_base_a, unit_size); + } + + tree seg_b_min = addr_base_b; + tree seg_b_max = fold_build_pointer_plus (addr_base_b, segment_length_b); + if (tree_int_cst_compare (DR_STEP (dr_b.dr), size_zero_node) < 0) + { + tree unit_size = TYPE_SIZE_UNIT (TREE_TYPE (DR_REF (dr_b.dr))); + seg_b_min = fold_build_pointer_plus (seg_b_max, unit_size); + seg_b_max = fold_build_pointer_plus (addr_base_b, unit_size); + } + *cond_expr + = fold_build2 (TRUTH_OR_EXPR, boolean_type_node, + fold_build2 (LE_EXPR, boolean_type_node, seg_a_max, seg_b_min), + fold_build2 (LE_EXPR, boolean_type_node, seg_b_max, seg_a_min)); +} + +/* Create a conditional expression that represents the run-time checks for + overlapping of address ranges represented by a list of data references + pairs passed in ALIAS_PAIRS. Data references are in LOOP. The returned + COND_EXPR is the conditional expression to be used in the if statement + that controls which version of the loop gets executed at runtime. */ + +void +create_runtime_alias_checks (struct loop *loop, + vec *alias_pairs, + tree * cond_expr) +{ + tree part_cond_expr; + + for (size_t i = 0, s = alias_pairs->length (); i < s; ++i) + { + const dr_with_seg_len& dr_a = (*alias_pairs)[i].first; + const dr_with_seg_len& dr_b = (*alias_pairs)[i].second; + + if (dump_enabled_p ()) + { + dump_printf (MSG_NOTE, "create runtime check for data references "); + dump_generic_expr (MSG_NOTE, TDF_SLIM, DR_REF (dr_a.dr)); + dump_printf (MSG_NOTE, " and "); + dump_generic_expr (MSG_NOTE, TDF_SLIM, DR_REF (dr_b.dr)); + dump_printf (MSG_NOTE, "\n"); + } + + /* Create condition expression for each pair data references. */ + create_intersect_range_checks (loop, &part_cond_expr, dr_a, dr_b); + if (*cond_expr) + *cond_expr = fold_build2 (TRUTH_AND_EXPR, boolean_type_node, + *cond_expr, part_cond_expr); + else + *cond_expr = part_cond_expr; + } +} + /* Check if OFFSET1 and OFFSET2 (DR_OFFSETs of some data-refs) are identical expressions. */ static bool diff --git a/gcc/tree-data-ref.h b/gcc/tree-data-ref.h index dbe8372c7f9..1d8e01dd7fe 100644 --- a/gcc/tree-data-ref.h +++ b/gcc/tree-data-ref.h @@ -371,6 +371,8 @@ extern bool dr_equal_offsets_p (struct data_reference *, extern int data_ref_compare_tree (tree, tree); extern void prune_runtime_alias_test_list (vec *, unsigned HOST_WIDE_INT); +extern void create_runtime_alias_checks (struct loop *, + vec *, tree*); /* Return true when the base objects of data references A and B are the same memory object. */ diff --git a/gcc/tree-vect-loop-manip.c b/gcc/tree-vect-loop-manip.c index 62b1fe8e42d..42b9f48dbf4 100644 --- a/gcc/tree-vect-loop-manip.c +++ b/gcc/tree-vect-loop-manip.c @@ -2044,194 +2044,6 @@ vect_create_cond_for_align_checks (loop_vec_info loop_vinfo, *cond_expr = part_cond_expr; } -/* Given LOOP's two data references and segment lengths described by DR_A - and DR_B, create expression checking if the two addresses ranges intersect - with each other based on index of the two addresses. This can only be - done if DR_A and DR_B referring to the same (array) object and the index - is the only difference. For example: - - DR_A DR_B - data-ref arr[i] arr[j] - base_object arr arr - index {i_0, +, 1}_loop {j_0, +, 1}_loop - - The addresses and their index are like: - - |<- ADDR_A ->| |<- ADDR_B ->| - -------------------------------------------------------> - | | | | | | | | | | - -------------------------------------------------------> - i_0 ... i_0+4 j_0 ... j_0+4 - - We can create expression based on index rather than address: - - (i_0 + 4 < j_0 || j_0 + 4 < i_0) - - Note evolution step of index needs to be considered in comparison. */ - -static bool -create_intersect_range_checks_index (struct loop *loop, tree *cond_expr, - const dr_with_seg_len& dr_a, - const dr_with_seg_len& dr_b) -{ - if (integer_zerop (DR_STEP (dr_a.dr)) - || integer_zerop (DR_STEP (dr_b.dr)) - || DR_NUM_DIMENSIONS (dr_a.dr) != DR_NUM_DIMENSIONS (dr_b.dr)) - return false; - - if (!tree_fits_uhwi_p (dr_a.seg_len) || !tree_fits_uhwi_p (dr_b.seg_len)) - return false; - - if (!tree_fits_shwi_p (DR_STEP (dr_a.dr))) - return false; - - if (!operand_equal_p (DR_BASE_OBJECT (dr_a.dr), DR_BASE_OBJECT (dr_b.dr), 0)) - return false; - - if (!operand_equal_p (DR_STEP (dr_a.dr), DR_STEP (dr_b.dr), 0)) - return false; - - gcc_assert (TREE_CODE (DR_STEP (dr_a.dr)) == INTEGER_CST); - - bool neg_step = tree_int_cst_compare (DR_STEP (dr_a.dr), size_zero_node) < 0; - unsigned HOST_WIDE_INT abs_step - = absu_hwi (tree_to_shwi (DR_STEP (dr_a.dr))); - - unsigned HOST_WIDE_INT seg_len1 = tree_to_uhwi (dr_a.seg_len); - unsigned HOST_WIDE_INT seg_len2 = tree_to_uhwi (dr_b.seg_len); - /* Infer the number of iterations with which the memory segment is accessed - by DR. In other words, alias is checked if memory segment accessed by - DR_A in some iterations intersect with memory segment accessed by DR_B - in the same amount iterations. - Note segnment length is a linear function of number of iterations with - DR_STEP as the coefficient. */ - unsigned HOST_WIDE_INT niter_len1 = (seg_len1 + abs_step - 1) / abs_step; - unsigned HOST_WIDE_INT niter_len2 = (seg_len2 + abs_step - 1) / abs_step; - - unsigned int i; - for (i = 0; i < DR_NUM_DIMENSIONS (dr_a.dr); i++) - { - tree access1 = DR_ACCESS_FN (dr_a.dr, i); - tree access2 = DR_ACCESS_FN (dr_b.dr, i); - /* Two indices must be the same if they are not scev, or not scev wrto - current loop being vecorized. */ - if (TREE_CODE (access1) != POLYNOMIAL_CHREC - || TREE_CODE (access2) != POLYNOMIAL_CHREC - || CHREC_VARIABLE (access1) != (unsigned)loop->num - || CHREC_VARIABLE (access2) != (unsigned)loop->num) - { - if (operand_equal_p (access1, access2, 0)) - continue; - - return false; - } - /* The two indices must have the same step. */ - if (!operand_equal_p (CHREC_RIGHT (access1), CHREC_RIGHT (access2), 0)) - return false; - - tree idx_step = CHREC_RIGHT (access1); - /* Index must have const step, otherwise DR_STEP won't be constant. */ - gcc_assert (TREE_CODE (idx_step) == INTEGER_CST); - /* Index must evaluate in the same direction as DR. */ - gcc_assert (!neg_step || tree_int_cst_sign_bit (idx_step) == 1); - - tree min1 = CHREC_LEFT (access1); - tree min2 = CHREC_LEFT (access2); - if (!types_compatible_p (TREE_TYPE (min1), TREE_TYPE (min2))) - return false; - - /* Ideally, alias can be checked against loop's control IV, but we - need to prove linear mapping between control IV and reference - index. Although that should be true, we check against (array) - index of data reference. Like segment length, index length is - linear function of the number of iterations with index_step as - the coefficient, i.e, niter_len * idx_step. */ - tree idx_len1 = fold_build2 (MULT_EXPR, TREE_TYPE (min1), idx_step, - build_int_cst (TREE_TYPE (min1), - niter_len1)); - tree idx_len2 = fold_build2 (MULT_EXPR, TREE_TYPE (min2), idx_step, - build_int_cst (TREE_TYPE (min2), - niter_len2)); - tree max1 = fold_build2 (PLUS_EXPR, TREE_TYPE (min1), min1, idx_len1); - tree max2 = fold_build2 (PLUS_EXPR, TREE_TYPE (min2), min2, idx_len2); - /* Adjust ranges for negative step. */ - if (neg_step) - { - min1 = fold_build2 (MINUS_EXPR, TREE_TYPE (min1), max1, idx_step); - max1 = fold_build2 (MINUS_EXPR, TREE_TYPE (min1), - CHREC_LEFT (access1), idx_step); - min2 = fold_build2 (MINUS_EXPR, TREE_TYPE (min2), max2, idx_step); - max2 = fold_build2 (MINUS_EXPR, TREE_TYPE (min2), - CHREC_LEFT (access2), idx_step); - } - tree part_cond_expr - = fold_build2 (TRUTH_OR_EXPR, boolean_type_node, - fold_build2 (LE_EXPR, boolean_type_node, max1, min2), - fold_build2 (LE_EXPR, boolean_type_node, max2, min1)); - if (*cond_expr) - *cond_expr = fold_build2 (TRUTH_AND_EXPR, boolean_type_node, - *cond_expr, part_cond_expr); - else - *cond_expr = part_cond_expr; - } - return true; -} - -/* Given two data references and segment lengths described by DR_A and DR_B, - create expression checking if the two addresses ranges intersect with - each other: - - ((DR_A_addr_0 + DR_A_segment_length_0) <= DR_B_addr_0) - || (DR_B_addr_0 + DER_B_segment_length_0) <= DR_A_addr_0)) */ - -static void -create_intersect_range_checks (struct loop *loop, tree *cond_expr, - const dr_with_seg_len& dr_a, - const dr_with_seg_len& dr_b) -{ - *cond_expr = NULL_TREE; - if (create_intersect_range_checks_index (loop, cond_expr, dr_a, dr_b)) - return; - - tree segment_length_a = dr_a.seg_len; - tree segment_length_b = dr_b.seg_len; - tree addr_base_a = DR_BASE_ADDRESS (dr_a.dr); - tree addr_base_b = DR_BASE_ADDRESS (dr_b.dr); - tree offset_a = DR_OFFSET (dr_a.dr), offset_b = DR_OFFSET (dr_b.dr); - - offset_a = fold_build2 (PLUS_EXPR, TREE_TYPE (offset_a), - offset_a, DR_INIT (dr_a.dr)); - offset_b = fold_build2 (PLUS_EXPR, TREE_TYPE (offset_b), - offset_b, DR_INIT (dr_b.dr)); - addr_base_a = fold_build_pointer_plus (addr_base_a, offset_a); - addr_base_b = fold_build_pointer_plus (addr_base_b, offset_b); - - tree seg_a_min = addr_base_a; - tree seg_a_max = fold_build_pointer_plus (addr_base_a, segment_length_a); - /* For negative step, we need to adjust address range by TYPE_SIZE_UNIT - bytes, e.g., int a[3] -> a[1] range is [a+4, a+16) instead of - [a, a+12) */ - if (tree_int_cst_compare (DR_STEP (dr_a.dr), size_zero_node) < 0) - { - tree unit_size = TYPE_SIZE_UNIT (TREE_TYPE (DR_REF (dr_a.dr))); - seg_a_min = fold_build_pointer_plus (seg_a_max, unit_size); - seg_a_max = fold_build_pointer_plus (addr_base_a, unit_size); - } - - tree seg_b_min = addr_base_b; - tree seg_b_max = fold_build_pointer_plus (addr_base_b, segment_length_b); - if (tree_int_cst_compare (DR_STEP (dr_b.dr), size_zero_node) < 0) - { - tree unit_size = TYPE_SIZE_UNIT (TREE_TYPE (DR_REF (dr_b.dr))); - seg_b_min = fold_build_pointer_plus (seg_b_max, unit_size); - seg_b_max = fold_build_pointer_plus (addr_base_b, unit_size); - } - *cond_expr - = fold_build2 (TRUTH_OR_EXPR, boolean_type_node, - fold_build2 (LE_EXPR, boolean_type_node, seg_a_max, seg_b_min), - fold_build2 (LE_EXPR, boolean_type_node, seg_b_max, seg_a_min)); -} - /* Function vect_create_cond_for_alias_checks. Create a conditional expression that represents the run-time checks for @@ -2257,36 +2069,12 @@ vect_create_cond_for_alias_checks (loop_vec_info loop_vinfo, tree * cond_expr) { vec comp_alias_ddrs = LOOP_VINFO_COMP_ALIAS_DDRS (loop_vinfo); - tree part_cond_expr; if (comp_alias_ddrs.is_empty ()) return; - struct loop *loop = LOOP_VINFO_LOOP (loop_vinfo); - for (size_t i = 0, s = comp_alias_ddrs.length (); i < s; ++i) - { - const dr_with_seg_len& dr_a = comp_alias_ddrs[i].first; - const dr_with_seg_len& dr_b = comp_alias_ddrs[i].second; - - if (dump_enabled_p ()) - { - dump_printf_loc (MSG_NOTE, vect_location, - "create runtime check for data references "); - dump_generic_expr (MSG_NOTE, TDF_SLIM, DR_REF (dr_a.dr)); - dump_printf (MSG_NOTE, " and "); - dump_generic_expr (MSG_NOTE, TDF_SLIM, DR_REF (dr_b.dr)); - dump_printf (MSG_NOTE, "\n"); - } - - /* Create condition expression for each pair data references. */ - create_intersect_range_checks (loop, &part_cond_expr, dr_a, dr_b); - if (*cond_expr) - *cond_expr = fold_build2 (TRUTH_AND_EXPR, boolean_type_node, - *cond_expr, part_cond_expr); - else - *cond_expr = part_cond_expr; - } - + create_runtime_alias_checks (LOOP_VINFO_LOOP (loop_vinfo), + &comp_alias_ddrs, cond_expr); if (dump_enabled_p ()) dump_printf_loc (MSG_NOTE, vect_location, "created %u versioning for alias checks.\n",