On Tue, Aug 13, 2024 at 7:34 PM Andi Kleen <a...@linux.intel.com> wrote: > > Andi Kleen <a...@firstfloor.org> writes: > > I wanted to ping this patch. I believe Richard ok'ed most of it earlier > but need an ok for the changes resulting from his review too > (but they were mostly only test suite and comment fixes > apart from some minor tweaks)
OK. Thanks, Richard. > -Andi > > > The gimple-if-to-switch pass converts if statements with > > multiple equal checks on the same value to a switch. This breaks > > vectorization which cannot handle switches. > > > > Teach the tree-if-conv pass used by the vectorizer to handle > > simple switch statements, like those created by if-to-switch earlier. > > These are switches that only have a single non default block, > > They are handled similar to COND in if conversion. > > > > This makes the vect-bitfield-read-1-not test fail. The test > > checks for a bitfield analysis failing, but it actually > > relied on the ifcvt erroring out early because the test > > is using a switch. The if conversion still does not > > work because the switch is not in a form that this > > patch can handle, but it fails much later and the bitfield > > analysis succeeds, which makes the test fail. I marked > > it xfail because it doesn't seem to be testing what it wants > > to test. > > > > [v2: Fix tests to run correctly. Update comments and commit log. > > Fix gimple switch accessor use.] > > > > gcc/ChangeLog: > > > > PR tree-opt/115866 > > * tree-if-conv.cc (if_convertible_switch_p): New function. > > (if_convertible_stmt_p): Check for switch. > > (get_loop_body_in_if_conv_order): Handle switch. > > (predicate_bbs): Likewise. > > (predicate_statements): Likewise. > > (remove_conditions_and_labels): Likewise. > > (ifcvt_split_critical_edges): Likewise. > > (ifcvt_local_dce): Likewise. > > > > gcc/testsuite/ChangeLog: > > > > * gcc.dg/vect/vect-switch-ifcvt-1.c: New test. > > * gcc.dg/vect/vect-switch-ifcvt-2.c: New test. > > * gcc.dg/vect/vect-switch-search-line-fast.c: New test. > > * gcc.dg/vect/vect-bitfield-read-1-not.c: Change to xfail. > > --- > > gcc/doc/cfg.texi | 4 +- > > .../gcc.dg/vect/vect-bitfield-read-1-not.c | 2 +- > > .../gcc.dg/vect/vect-switch-ifcvt-1.c | 115 ++++++++++++++++++ > > .../gcc.dg/vect/vect-switch-ifcvt-2.c | 49 ++++++++ > > .../vect/vect-switch-search-line-fast.c | 17 +++ > > gcc/tree-if-conv.cc | 93 +++++++++++++- > > 6 files changed, 272 insertions(+), 8 deletions(-) > > create mode 100644 gcc/testsuite/gcc.dg/vect/vect-switch-ifcvt-1.c > > create mode 100644 gcc/testsuite/gcc.dg/vect/vect-switch-ifcvt-2.c > > create mode 100644 gcc/testsuite/gcc.dg/vect/vect-switch-search-line-fast.c > > > > diff --git a/gcc/doc/cfg.texi b/gcc/doc/cfg.texi > > index 9a22420f91f..a6f2b9f97d6 100644 > > --- a/gcc/doc/cfg.texi > > +++ b/gcc/doc/cfg.texi > > @@ -83,13 +83,13 @@ lexicographical order, except @code{ENTRY_BLOCK} and > > @code{EXIT_BLOCK}. > > The macro @code{FOR_ALL_BB} also visits all basic blocks in > > lexicographical order, including @code{ENTRY_BLOCK} and @code{EXIT_BLOCK}. > > > > -@findex post_order_compute, inverted_post_order_compute, > > walk_dominator_tree > > +@findex post_order_compute, inverted_post_order_compute, dom_walker::walk > > The functions @code{post_order_compute} and > > @code{inverted_post_order_compute} > > can be used to compute topological orders of the CFG. The orders are > > stored as vectors of basic block indices. The @code{BASIC_BLOCK} array > > can be used to iterate each basic block by index. > > Dominator traversals are also possible using > > -@code{walk_dominator_tree}. Given two basic blocks A and B, block A > > +@code{dom_walker::walk}. Given two basic blocks A and B, block A > > dominates block B if A is @emph{always} executed before B@. > > > > Each @code{basic_block} also contains pointers to the first > > diff --git a/gcc/testsuite/gcc.dg/vect/vect-bitfield-read-1-not.c > > b/gcc/testsuite/gcc.dg/vect/vect-bitfield-read-1-not.c > > index 0d91067ebb2..85f4de8464a 100644 > > --- a/gcc/testsuite/gcc.dg/vect/vect-bitfield-read-1-not.c > > +++ b/gcc/testsuite/gcc.dg/vect/vect-bitfield-read-1-not.c > > @@ -55,6 +55,6 @@ int main (void) > > return 0; > > } > > > > -/* { dg-final { scan-tree-dump-not "Bitfield OK to lower." "ifcvt" } } */ > > +/* { dg-final { scan-tree-dump-times "Bitfield OK to lower." 0 "ifcvt" { > > xfail *-*-* } } } */ > > > > > > diff --git a/gcc/testsuite/gcc.dg/vect/vect-switch-ifcvt-1.c > > b/gcc/testsuite/gcc.dg/vect/vect-switch-ifcvt-1.c > > new file mode 100644 > > index 00000000000..f5352ef8ed7 > > --- /dev/null > > +++ b/gcc/testsuite/gcc.dg/vect/vect-switch-ifcvt-1.c > > @@ -0,0 +1,115 @@ > > +/* { dg-require-effective-target vect_int } */ > > +#include "tree-vect.h" > > + > > +extern void abort (void); > > + > > +int > > +f1 (char *s) > > +{ > > + int c = 0; > > + int i; > > + for (i = 0; i < 64; i++) > > + { > > + switch (*s) > > + { > > + case ',': > > + case '|': > > + c++; > > + } > > + s++; > > + } > > + return c; > > +} > > + > > +int > > +f2 (char *s) > > +{ > > + int c = 0; > > + int i; > > + for (i = 0; i < 64; i++) > > + { > > + if (*s != '#') > > + { > > + switch (*s) > > + { > > + case ',': > > + case '|': > > + c++; > > + } > > + } > > + s++; > > + } > > + return c; > > +} > > + > > +int > > +f3 (char *s) > > +{ > > + int c = 0; > > + int i; > > + for (i = 0; i < 64; i++) > > + { > > + if (*s != '#') > > + if (*s == ',' || *s == '|' || *s == '@' || *s == '*') > > + c++; > > + s++; > > + } > > + return c; > > +} > > + > > + > > +int > > +f4 (char *s) > > +{ > > + int c = 0; > > + int i; > > + for (i = 0; i < 64; i++) > > + { > > + if (*s == ',' || *s == '|' || *s == '@' || *s == '*') > > + c++; > > + s++; > > + } > > + return c; > > +} > > + > > +#define CHECK(f, str, res) \ > > + __builtin_strcpy(buf, str); n = f(buf); if (n != res) abort(); > > + > > +int > > +main () > > +{ > > + int n; > > + char buf[64]; > > + > > + __builtin_memset (buf, 0, sizeof buf); > > + > > + check_vect (); > > + > > + CHECK (f1, ",,,,,,,,,,", 10); > > + CHECK (f1, "||||||||||", 10); > > + CHECK (f1, "aaaaaaaaaa", 0); > > + CHECK (f1, "", 0); > > + CHECK (f1, ",|,|xxxxxx", 4); > > + > > + CHECK (f2, ",,,,,,,,,,", 10); > > + CHECK (f2, "||||||||||", 10); > > + CHECK (f2, "aaaaaaaaaa", 0); > > + CHECK (f2, "", 0); > > + CHECK (f2, ",|,|xxxxxx", 4); > > + > > + CHECK (f3, ",,,,,,,,,,", 10); > > + CHECK (f3, "||||||||||", 10); > > + CHECK (f3, "aaaaaaaaaa", 0); > > + CHECK (f3, "", 0); > > + CHECK (f3, ",|,|xxxxxx", 4); > > + > > + CHECK (f4, ",,,,,,,,,,", 10); > > + CHECK (f4, "||||||||||", 10); > > + CHECK (f4, "aaaaaaaaaa", 0); > > + CHECK (f4, "", 0); > > + CHECK (f4, ",|,|xxxxxx", 4); > > + > > + return 0; > > +} > > + > > +/* { dg-final { scan-tree-dump-times "vectorized 1 loops" 4 "vect" } } */ > > diff --git a/gcc/testsuite/gcc.dg/vect/vect-switch-ifcvt-2.c > > b/gcc/testsuite/gcc.dg/vect/vect-switch-ifcvt-2.c > > new file mode 100644 > > index 00000000000..f0fcb43e177 > > --- /dev/null > > +++ b/gcc/testsuite/gcc.dg/vect/vect-switch-ifcvt-2.c > > @@ -0,0 +1,49 @@ > > +/* { dg-do compile } */ > > +/* { dg-require-effective-target vect_int } */ > > + > > +/* Check for cases currently not supported by switch tree if conversion. > > */ > > + > > +int > > +f1 (char *s) > > +{ > > + int c = 0; > > + int i; > > + for (i = 0; i < 64; i++) > > + { > > + switch (*s) > > + { > > + case ',': > > + case '|': > > + c++; > > + break; > > + case '^': > > + c += 2; > > + break; > > + } > > + s++; > > + } > > + return c; > > +} > > + > > +int > > +f2 (char *s) > > +{ > > + int c = 0; > > + int i; > > + for (i = 0; i < 64; i++) > > + { > > + switch (*s) > > + { > > + case ',': > > + case '|': > > + c++; > > + /*fallthrough*/ > > + default: > > + c+=2; > > + } > > + s++; > > + } > > + return c; > > +} > > + > > +/* { dg-final { scan-tree-dump-times "vectorized 1 loops" 0 "vect" } } */ > > diff --git a/gcc/testsuite/gcc.dg/vect/vect-switch-search-line-fast.c > > b/gcc/testsuite/gcc.dg/vect/vect-switch-search-line-fast.c > > new file mode 100644 > > index 00000000000..15f3a4ef38a > > --- /dev/null > > +++ b/gcc/testsuite/gcc.dg/vect/vect-switch-search-line-fast.c > > @@ -0,0 +1,17 @@ > > +/* PR116126 -- once this works use this version in libcpp/lex.c. > > + This also requires working value range propagation for s/end. */ > > +/* { dg-do compile } */ > > +/* { dg-require-effective-target vect_int } */ > > + > > +const unsigned char *search_line_fast2 (const unsigned char *s, > > + const unsigned char *end) > > +{ > > + while (s < end) { > > + if (*s == '\n' || *s == '\r' || *s == '\\' || *s == '?') > > + break; > > + s++; > > + } > > + return s; > > +} > > + > > +/* { dg-final { scan-tree-dump-times "vectorized 1 loops" 1 "vect" { xfail > > *-*-* } } } */ > > diff --git a/gcc/tree-if-conv.cc b/gcc/tree-if-conv.cc > > index 57992b6deca..5aac65cfbd1 100644 > > --- a/gcc/tree-if-conv.cc > > +++ b/gcc/tree-if-conv.cc > > @@ -124,6 +124,7 @@ along with GCC; see the file COPYING3. If not see > > #include "tree-vectorizer.h" > > #include "tree-eh.h" > > #include "cgraph.h" > > +#include "print-tree.h" > > > > /* For lang_hooks.types.type_for_mode. */ > > #include "langhooks.h" > > @@ -1082,11 +1083,35 @@ if_convertible_gimple_assign_stmt_p (gimple *stmt, > > return true; > > } > > > > +/* Return true when SW switch statement is equivalent to cond, that > > + all non default labels point to the same label. > > + > > + Fallthrough is not checked for and could even happen > > + with cond (using goto), so is handled. > > + > > + This is intended for switches created by the if-switch-conversion > > + pass, but can handle some programmer supplied cases too. */ > > + > > +static bool > > +if_convertible_switch_p (gswitch *sw) > > +{ > > + if (gimple_switch_num_labels (sw) <= 1) > > + return false; > > + tree label = CASE_LABEL (gimple_switch_label (sw, 1)); > > + for (unsigned i = 1; i < gimple_switch_num_labels (sw); i++) > > + { > > + if (CASE_LABEL (gimple_switch_label (sw, i)) != label) > > + return false; > > + } > > + return true; > > +} > > + > > /* Return true when STMT is if-convertible. > > > > A statement is if-convertible if: > > - it is an if-convertible GIMPLE_ASSIGN, > > - it is a GIMPLE_LABEL or a GIMPLE_COND, > > + - it is a switch equivalent to COND > > - it is builtins call, > > - it is a call to a function with a SIMD clone. */ > > > > @@ -1100,6 +1125,9 @@ if_convertible_stmt_p (gimple *stmt, > > vec<data_reference_p> refs) > > case GIMPLE_COND: > > return true; > > > > + case GIMPLE_SWITCH: > > + return if_convertible_switch_p (as_a <gswitch *> (stmt)); > > + > > case GIMPLE_ASSIGN: > > return if_convertible_gimple_assign_stmt_p (stmt, refs); > > > > @@ -1327,6 +1355,7 @@ get_loop_body_in_if_conv_order (const class loop > > *loop) > > case GIMPLE_CALL: > > case GIMPLE_DEBUG: > > case GIMPLE_COND: > > + case GIMPLE_SWITCH: > > gimple_set_uid (gsi_stmt (gsi), 0); > > break; > > default: > > @@ -1426,6 +1455,58 @@ predicate_bbs (loop_p loop) > > cond = NULL_TREE; > > } > > > > + /* Assumes the limited COND like switches checked for earlier. */ > > + else if (gswitch *sw = safe_dyn_cast <gswitch *> (*gsi_last_bb (bb))) > > + { > > + location_t loc = gimple_location (*gsi_last_bb (bb)); > > + > > + tree default_label = CASE_LABEL (gimple_switch_default_label (sw)); > > + tree cond_label = CASE_LABEL (gimple_switch_label (sw, 1)); > > + > > + edge false_edge = find_edge (bb, label_to_block (cfun, > > default_label)); > > + edge true_edge = find_edge (bb, label_to_block (cfun, cond_label)); > > + > > + /* Create chain of switch tests for each case. */ > > + tree switch_cond = NULL_TREE; > > + tree index = gimple_switch_index (sw); > > + for (unsigned i = 1; i < gimple_switch_num_labels (sw); i++) > > + { > > + tree label = gimple_switch_label (sw, i); > > + tree case_cond; > > + if (CASE_HIGH (label)) > > + { > > + tree low = build2_loc (loc, GE_EXPR, > > + boolean_type_node, > > + index, CASE_LOW (label)); > > + tree high = build2_loc (loc, LE_EXPR, > > + boolean_type_node, > > + index, CASE_HIGH (label)); > > + case_cond = build2_loc (loc, TRUTH_AND_EXPR, > > + boolean_type_node, > > + low, high); > > + } > > + else > > + case_cond = build2_loc (loc, EQ_EXPR, > > + boolean_type_node, > > + index, > > + CASE_LOW (gimple_switch_label (sw, > > i))); > > + if (i > 1) > > + switch_cond = build2_loc (loc, TRUTH_OR_EXPR, > > + boolean_type_node, > > + case_cond, switch_cond); > > + else > > + switch_cond = case_cond; > > + } > > + > > + add_to_dst_predicate_list (loop, true_edge, unshare_expr (cond), > > + unshare_expr (switch_cond)); > > + switch_cond = build1_loc (loc, TRUTH_NOT_EXPR, boolean_type_node, > > + unshare_expr (switch_cond)); > > + add_to_dst_predicate_list (loop, false_edge, > > + unshare_expr (cond), switch_cond); > > + cond = NULL_TREE; > > + } > > + > > /* If current bb has only one successor, then consider it as an > > unconditional goto. */ > > if (single_succ_p (bb)) > > @@ -2852,9 +2933,9 @@ predicate_statements (loop_p loop) > > } > > } > > > > -/* Remove all GIMPLE_CONDs and GIMPLE_LABELs of all the basic blocks > > - other than the exit and latch of the LOOP. Also resets the > > - GIMPLE_DEBUG information. */ > > +/* Remove all GIMPLE_CONDs and GIMPLE_LABELs and GIMPLE_SWITCH of all > > + the basic blocks other than the exit and latch of the LOOP. Also > > + resets the GIMPLE_DEBUG information. */ > > > > static void > > remove_conditions_and_labels (loop_p loop) > > @@ -2875,6 +2956,7 @@ remove_conditions_and_labels (loop_p loop) > > { > > case GIMPLE_COND: > > case GIMPLE_LABEL: > > + case GIMPLE_SWITCH: > > gsi_remove (&gsi, true); > > break; > > > > @@ -3265,7 +3347,8 @@ ifcvt_split_critical_edges (class loop *loop, bool > > aggressive_if_conv) > > continue; > > > > /* Skip basic blocks not ending with conditional branch. */ > > - if (!safe_is_a <gcond *> (*gsi_last_bb (bb))) > > + if (!safe_is_a <gcond *> (*gsi_last_bb (bb)) > > + && !safe_is_a <gswitch *> (*gsi_last_bb (bb))) > > continue; > > > > FOR_EACH_EDGE (e, ei, bb->succs) > > @@ -3330,7 +3413,7 @@ ifcvt_local_dce (class loop *loop) > > continue; > > } > > code = gimple_code (stmt); > > - if (code == GIMPLE_COND || code == GIMPLE_CALL) > > + if (code == GIMPLE_COND || code == GIMPLE_CALL || code == > > GIMPLE_SWITCH) > > { > > gimple_set_plf (stmt, GF_PLF_2, true); > > worklist.safe_push (stmt);