Hi! switchconv uses unsigned_type_for to get unsigned type to perform computations in, which is fine if you just do a comparison in that type or similar, but not when actually constructing range-like checks or doing further arithmetics on the type. The reassoc and fold-const range test optimization has range_check_type function for that, this patch makes use of that function, which among other things uses an INTEGER_TYPE instead of enums/booleans and verifies the INTEGER_TYPE minimum/maximum to make sure it properly wraps around. One issue is that this function can return NULL on some weird integral types (perhaps Ada special integers), so we need to punt in that case.
Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? 2019-08-29 Jakub Jelinek <ja...@redhat.com> PR tree-optimization/91351 * tree-cfg.c (generate_range_test): Use range_check_type instead of unsigned_type_for. * tree-cfgcleanup.c (convert_single_case_switch): Punt if range_check_type returns NULL. * tree-switch-conversion.c (switch_conversion::build_one_array): Use range_check_type instead of unsigned_type_for, don't perform linear opt if it returns NULL. (bit_test_cluster::find_bit_tests): Formatting fix. (bit_test_cluster::emit): Use range_check_type instead of unsigned_type_for. (switch_decision_tree::try_switch_expansion): Punt if range_check_type returns NULL. * g++.dg/opt/pr91351.C: New test. --- gcc/tree-cfg.c.jj 2019-07-29 12:56:38.971248016 +0200 +++ gcc/tree-cfg.c 2019-08-28 19:37:50.843262628 +0200 @@ -9221,7 +9221,7 @@ generate_range_test (basic_block bb, tre tree *lhs, tree *rhs) { tree type = TREE_TYPE (index); - tree utype = unsigned_type_for (type); + tree utype = range_check_type (type); low = fold_convert (utype, low); high = fold_convert (utype, high); --- gcc/tree-cfgcleanup.c.jj 2019-04-24 10:10:22.816535073 +0200 +++ gcc/tree-cfgcleanup.c 2019-08-28 19:39:40.032680646 +0200 @@ -101,6 +101,8 @@ convert_single_case_switch (gswitch *swt if (high) { tree lhs, rhs; + if (range_check_type (TREE_TYPE (index)) == NULL_TREE) + return false; generate_range_test (bb, index, low, high, &lhs, &rhs); cond = gimple_build_cond (LE_EXPR, lhs, rhs, NULL_TREE, NULL_TREE); } --- gcc/tree-switch-conversion.c.jj 2019-07-10 15:53:01.148520370 +0200 +++ gcc/tree-switch-conversion.c 2019-08-28 19:45:18.062783134 +0200 @@ -605,7 +605,9 @@ switch_conversion::build_one_array (int vec<constructor_elt, va_gc> *constructor = m_constructors[num]; wide_int coeff_a, coeff_b; bool linear_p = contains_linear_function_p (constructor, &coeff_a, &coeff_b); - if (linear_p) + tree type; + if (linear_p + && (type = range_check_type (TREE_TYPE ((*constructor)[0].value)))) { if (dump_file && coeff_a.to_uhwi () > 0) fprintf (dump_file, "Linear transformation with A = %" PRId64 @@ -613,13 +615,12 @@ switch_conversion::build_one_array (int coeff_b.to_shwi ()); /* We must use type of constructor values. */ - tree t = unsigned_type_for (TREE_TYPE ((*constructor)[0].value)); gimple_seq seq = NULL; - tree tmp = gimple_convert (&seq, t, m_index_expr); - tree tmp2 = gimple_build (&seq, MULT_EXPR, t, - wide_int_to_tree (t, coeff_a), tmp); - tree tmp3 = gimple_build (&seq, PLUS_EXPR, t, tmp2, - wide_int_to_tree (t, coeff_b)); + tree tmp = gimple_convert (&seq, type, m_index_expr); + tree tmp2 = gimple_build (&seq, MULT_EXPR, type, + wide_int_to_tree (type, coeff_a), tmp); + tree tmp3 = gimple_build (&seq, PLUS_EXPR, type, tmp2, + wide_int_to_tree (type, coeff_b)); tree tmp4 = gimple_convert (&seq, TREE_TYPE (name), tmp3); gsi_insert_seq_before (&gsi, seq, GSI_SAME_STMT); load = gimple_build_assign (name, tmp4); @@ -1351,7 +1352,7 @@ bit_test_cluster::find_bit_tests (vec<cl entire)); } else - for (int i = end - 1; i >= start; i--) + for (int i = end - 1; i >= start; i--) output.safe_push (clusters[i]); end = start; @@ -1484,7 +1485,7 @@ bit_test_cluster::emit (tree index_expr, unsigned int i, j, k; unsigned int count; - tree unsigned_index_type = unsigned_type_for (index_type); + tree unsigned_index_type = range_check_type (index_type); gimple_stmt_iterator gsi; gassign *shift_stmt; @@ -1794,7 +1795,8 @@ switch_decision_tree::try_switch_expansi tree index_type = TREE_TYPE (index_expr); basic_block bb = gimple_bb (m_switch); - if (gimple_switch_num_labels (m_switch) == 1) + if (gimple_switch_num_labels (m_switch) == 1 + || range_check_type (index_type) == NULL_TREE) return false; /* Find the default case target label. */ --- gcc/testsuite/g++.dg/opt/pr91351.C.jj 2019-08-28 19:53:50.946352281 +0200 +++ gcc/testsuite/g++.dg/opt/pr91351.C 2019-08-28 19:53:30.277651740 +0200 @@ -0,0 +1,38 @@ +// PR tree-optimization/91351 +// { dg-do run } +// { dg-options "-O2 -fstrict-enums" } + +enum E { e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, + e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23, e24, e25 }; + +__attribute__((noipa)) void +foo () +{ + __builtin_abort (); +} + +__attribute__((noipa)) void +bar () +{ +} + +__attribute__((noipa)) void +baz (E e) +{ + switch (e) + { + case e11: + case e12: + case e13: foo (); break; + case e24: break; + case e14: + case e15: break; + default: bar (); break; + } +} + +int +main () +{ + baz (e3); +} Jakub