https://gcc.gnu.org/g:e60c1793efd81571e408e875f5e42f441ea311e8
commit r16-1561-ge60c1793efd81571e408e875f5e42f441ea311e8 Author: Andrew MacLeod <amacl...@redhat.com> Date: Wed May 28 11:16:05 2025 -0400 Simplify switches utilizing subranges. Adjust simplify_switch_using_ranges to use irange rather than relying on the older legacy_range mechaism. PR tree-optimization/119039 gcc/ * vr-values.cc (simplify_using_ranges::legacy_fold_cond): Remove. (simplify_using_ranges::simplify_switch_using_ranges): Adjust. gcc/testsuite/ * gcc.dg/pr119039-1.c: New. * gcc.dg/tree-ssa/ssa-dom-thread-7.c: Adjust thread counts. Diff: --- gcc/testsuite/gcc.dg/pr119039-1.c | 32 +++ gcc/testsuite/gcc.dg/tree-ssa/ssa-dom-thread-7.c | 4 +- gcc/vr-values.cc | 281 ++++++----------------- 3 files changed, 106 insertions(+), 211 deletions(-) diff --git a/gcc/testsuite/gcc.dg/pr119039-1.c b/gcc/testsuite/gcc.dg/pr119039-1.c new file mode 100644 index 000000000000..5ca656388537 --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr119039-1.c @@ -0,0 +1,32 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-evrp" } */ + +extern void foo (void); +extern void bar (void); +extern void baz (void); + +/* Tests the ability to remove cases that are subranges. */ + +void +test (int i) +{ + if (i < 0 || i > 45) + return; + if (i >= 7 && i <= 8) + return; + + switch (i) + { + case 1: + bar (); + break; + case 7 ... 8: + foo (); + case 14: + baz (); + break; + default: + break; + } +} +/* { dg-final { scan-tree-dump-not "foo" "evrp" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/ssa-dom-thread-7.c b/gcc/testsuite/gcc.dg/tree-ssa/ssa-dom-thread-7.c index 59891f29132c..1c2cfa4fa8d5 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/ssa-dom-thread-7.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/ssa-dom-thread-7.c @@ -11,8 +11,8 @@ to change decisions in switch expansion which in turn can expose new jump threading opportunities. Skip the later tests on aarch64. */ /* { dg-final { scan-tree-dump-not "Jumps threaded" "dom3" { target { ! aarch64*-*-* } } } } */ -/* { dg-final { scan-tree-dump "Jumps threaded: 10" "thread2" { target { ! aarch64*-*-* } } } } */ -/* { dg-final { scan-tree-dump "Jumps threaded: 17" "thread2" { target { aarch64*-*-* } } } } */ +/* { dg-final { scan-tree-dump "Jumps threaded: 8" "thread2" { target { ! aarch64*-*-* } } } } */ +/* { dg-final { scan-tree-dump "Jumps threaded: 8" "thread2" { target { aarch64*-*-* } } } } */ enum STATE { S0=0, diff --git a/gcc/vr-values.cc b/gcc/vr-values.cc index 4c787593b95a..799f1bfd91d8 100644 --- a/gcc/vr-values.cc +++ b/gcc/vr-values.cc @@ -513,85 +513,6 @@ simplify_using_ranges::legacy_fold_cond (gcond *stmt, edge *taken_edge_p) } } -/* Searches the case label vector VEC for the ranges of CASE_LABELs that are - used in range VR. The indices are placed in MIN_IDX1, MAX_IDX, MIN_IDX2 and - MAX_IDX2. If the ranges of CASE_LABELs are empty then MAX_IDX1 < MIN_IDX1. - Returns true if the default label is not needed. */ - -static bool -find_case_label_ranges (gswitch *stmt, const irange *vr, - size_t *min_idx1, size_t *max_idx1, - size_t *min_idx2, size_t *max_idx2) -{ - size_t i, j, k, l; - unsigned int n = gimple_switch_num_labels (stmt); - bool take_default; - tree case_low, case_high; - tree min, max; - value_range_kind kind = get_legacy_range (*vr, min, max); - - gcc_checking_assert (!vr->varying_p () && !vr->undefined_p ()); - - take_default = !find_case_label_range (stmt, min, max, &i, &j); - - /* Set second range to empty. */ - *min_idx2 = 1; - *max_idx2 = 0; - - if (kind == VR_RANGE) - { - *min_idx1 = i; - *max_idx1 = j; - return !take_default; - } - - /* Set first range to all case labels. */ - *min_idx1 = 1; - *max_idx1 = n - 1; - - if (i > j) - return false; - - /* Make sure all the values of case labels [i , j] are contained in - range [MIN, MAX]. */ - case_low = CASE_LOW (gimple_switch_label (stmt, i)); - case_high = CASE_HIGH (gimple_switch_label (stmt, j)); - if (tree_int_cst_compare (case_low, min) < 0) - i += 1; - if (case_high != NULL_TREE - && tree_int_cst_compare (max, case_high) < 0) - j -= 1; - - if (i > j) - return false; - - /* If the range spans case labels [i, j], the corresponding anti-range spans - the labels [1, i - 1] and [j + 1, n - 1]. */ - k = j + 1; - l = n - 1; - if (k > l) - { - k = 1; - l = 0; - } - - j = i - 1; - i = 1; - if (i > j) - { - i = k; - j = l; - k = 1; - l = 0; - } - - *min_idx1 = i; - *max_idx1 = j; - *min_idx2 = k; - *max_idx2 = l; - return false; -} - /* Simplify boolean operations if the source is known to be already a boolean. */ bool @@ -1374,157 +1295,99 @@ bool simplify_using_ranges::simplify_switch_using_ranges (gswitch *stmt) { tree op = gimple_switch_index (stmt); - int_range_max vr; - bool take_default; + tree type = TREE_TYPE (op); + int_range_max op_range (type); + int_range_max default_range (type); + auto_vec<unsigned> cases; + cases.truncate (0); edge e; - edge_iterator ei; - size_t i = 0, j = 0, n, n2; - tree vec2; switch_update su; - size_t k = 1, l = 0; - if (TREE_CODE (op) == SSA_NAME) - { - if (!query->range_of_expr (vr, op, stmt) - || vr.varying_p () || vr.undefined_p ()) - return false; - - /* Find case label for min/max of the value range. */ - take_default = !find_case_label_ranges (stmt, &vr, &i, &j, &k, &l); - } - else if (TREE_CODE (op) == INTEGER_CST) - { - take_default = !find_case_label_index (stmt, 1, op, &i); - if (take_default) - { - i = 1; - j = 0; - } - else - { - j = i; - } - } - else + // Abort if we don't have a useful range for the switch index. + if (!query->range_of_expr (op_range, op, stmt) + || op_range.varying_p () || op_range.undefined_p ()) return false; - n = gimple_switch_num_labels (stmt); + // Default range starts with full known range of op. + default_range = op_range; + edge default_edge = gimple_switch_default_edge (cfun, stmt); - /* We can truncate the case label ranges that partially overlap with OP's - value range. */ - size_t min_idx = 1, max_idx = 0; - tree min, max; - value_range_kind kind = get_legacy_range (vr, min, max); - if (!vr.undefined_p ()) - find_case_label_range (stmt, min, max, &min_idx, &max_idx); - if (min_idx <= max_idx) + unsigned x, lim = gimple_switch_num_labels (stmt); + for (x = 1; x < lim; x++) { - tree min_label = gimple_switch_label (stmt, min_idx); - tree max_label = gimple_switch_label (stmt, max_idx); + e = gimple_switch_edge (cfun, stmt, x); + tree label = gimple_switch_label (stmt, x); + + // If this edge is the same as the default edge, do nothing else. + if (e == default_edge) + continue; + // Ada sometimes has mismatched labels and index. Just bail. + if (TREE_TYPE (CASE_LOW (label)) != type) + return false; - /* Avoid changing the type of the case labels when truncating. */ - tree case_label_type = TREE_TYPE (CASE_LOW (min_label)); - tree vr_min = fold_convert (case_label_type, min); - tree vr_max = fold_convert (case_label_type, max); + wide_int low = wi::to_wide (CASE_LOW (label)); + wide_int high; + // Singleton cases have no CASE_HIGH. + tree tree_high = CASE_HIGH (label); + if (tree_high) + high = wi::to_wide (tree_high); + else + high = low; - if (kind == VR_RANGE) - { - /* If OP's value range is [2,8] and the low label range is - 0 ... 3, truncate the label's range to 2 .. 3. */ - if (tree_int_cst_compare (CASE_LOW (min_label), vr_min) < 0 - && CASE_HIGH (min_label) != NULL_TREE - && tree_int_cst_compare (CASE_HIGH (min_label), vr_min) >= 0) - CASE_LOW (min_label) = vr_min; - - /* If OP's value range is [2,8] and the high label range is - 7 ... 10, truncate the label's range to 7 .. 8. */ - if (tree_int_cst_compare (CASE_LOW (max_label), vr_max) <= 0 - && CASE_HIGH (max_label) != NULL_TREE - && tree_int_cst_compare (CASE_HIGH (max_label), vr_max) > 0) - CASE_HIGH (max_label) = vr_max; - } - else if (kind == VR_ANTI_RANGE) + // If the case range is fully contained in op_range, leave the + // case as it is, otherwise adjust the labels. + int_range_max case_range (type, low, high); + if (case_range.intersect (op_range)) { - tree one_cst = build_one_cst (case_label_type); - - if (min_label == max_label) - { - /* If OP's value range is ~[7,8] and the label's range is - 7 ... 10, truncate the label's range to 9 ... 10. */ - if (tree_int_cst_compare (CASE_LOW (min_label), vr_min) == 0 - && CASE_HIGH (min_label) != NULL_TREE - && tree_int_cst_compare (CASE_HIGH (min_label), vr_max) > 0) - CASE_LOW (min_label) - = int_const_binop (PLUS_EXPR, vr_max, one_cst); - - /* If OP's value range is ~[7,8] and the label's range is - 5 ... 8, truncate the label's range to 5 ... 6. */ - if (tree_int_cst_compare (CASE_LOW (min_label), vr_min) < 0 - && CASE_HIGH (min_label) != NULL_TREE - && tree_int_cst_compare (CASE_HIGH (min_label), vr_max) == 0) - CASE_HIGH (min_label) - = int_const_binop (MINUS_EXPR, vr_min, one_cst); - } + // If none of the label is in op_range, skip this label. + if (case_range.undefined_p ()) + continue; + + // Part of the label is in op_range, but not all of it. CASE_RANGE + // contains the part that is. Adjust the case range to + // the new min/max. + if (case_range.lower_bound () != low) + CASE_LOW (label) = wide_int_to_tree (type, + case_range.lower_bound ()); + if (case_range.singleton_p ()) + CASE_HIGH (label) = NULL_TREE; else - { - /* If OP's value range is ~[2,8] and the low label range is - 0 ... 3, truncate the label's range to 0 ... 1. */ - if (tree_int_cst_compare (CASE_LOW (min_label), vr_min) < 0 - && CASE_HIGH (min_label) != NULL_TREE - && tree_int_cst_compare (CASE_HIGH (min_label), vr_min) >= 0) - CASE_HIGH (min_label) - = int_const_binop (MINUS_EXPR, vr_min, one_cst); - - /* If OP's value range is ~[2,8] and the high label range is - 7 ... 10, truncate the label's range to 9 ... 10. */ - if (tree_int_cst_compare (CASE_LOW (max_label), vr_max) <= 0 - && CASE_HIGH (max_label) != NULL_TREE - && tree_int_cst_compare (CASE_HIGH (max_label), vr_max) > 0) - CASE_LOW (max_label) - = int_const_binop (PLUS_EXPR, vr_max, one_cst); - } + if (case_range.upper_bound () != high) + CASE_HIGH (label) = wide_int_to_tree (type, + case_range.upper_bound ()); } - - /* Canonicalize singleton case ranges. */ - if (tree_int_cst_equal (CASE_LOW (min_label), CASE_HIGH (min_label))) - CASE_HIGH (min_label) = NULL_TREE; - if (tree_int_cst_equal (CASE_LOW (max_label), CASE_HIGH (max_label))) - CASE_HIGH (max_label) = NULL_TREE; + // Add case label to the keep list. + cases.safe_push (x); + // Remove case_range from needing to be handled by the default. + case_range.invert (); + default_range.intersect (case_range); } - /* We can also eliminate case labels that lie completely outside OP's value - range. */ - - /* Bail out if this is just all edges taken. */ - if (i == 1 - && j == n - 1 - && take_default) + // An undefined DEFAULT range means the current default case is not needed. + unsigned idx = default_range.undefined_p () ? 0 : 1; + unsigned vec_size = cases.length () + idx; + if (vec_size == lim) return false; - /* Build a new vector of taken case labels. */ - vec2 = make_tree_vec (j - i + 1 + l - k + 1 + (int)take_default); - n2 = 0; - - /* Add the default edge, if necessary. */ - if (take_default) - TREE_VEC_ELT (vec2, n2++) = gimple_switch_default_label (stmt); - - for (; i <= j; ++i, ++n2) - TREE_VEC_ELT (vec2, n2) = gimple_switch_label (stmt, i); - - for (; k <= l; ++k, ++n2) - TREE_VEC_ELT (vec2, n2) = gimple_switch_label (stmt, k); + tree vec2 = make_tree_vec (vec_size); + // Add default label if there is one. + if (idx) + { + TREE_VEC_ELT (vec2, 0) = gimple_switch_default_label (stmt); + e = gimple_switch_edge (cfun, stmt, 0); + e->aux = (void *)-1; + } - /* Mark needed edges. */ - for (i = 0; i < n2; ++i) + for (x = 0; x < cases.length (); x++) { - e = find_edge (gimple_bb (stmt), - label_to_block (cfun, - CASE_LABEL (TREE_VEC_ELT (vec2, i)))); - e->aux = (void *)-1; + unsigned swi = cases[x]; + TREE_VEC_ELT (vec2, idx++) = gimple_switch_label (stmt, swi); + e = gimple_switch_edge (cfun, stmt, swi); + e->aux = (void *)-1; } /* Queue not needed edges for later removal. */ + edge_iterator ei; FOR_EACH_EDGE (e, ei, gimple_bb (stmt)->succs) { if (e->aux == (void *)-1)