diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 8a38316..2637309 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,8 @@ +2013-10-15 Cong Hou + + * tree-vect-loop-manip.c (vect_loop_versioning): Hoist loop invariant + statement that contains data refs with zero-step. + 2013-10-14 David Malcolm * dumpfile.h (gcc::dump_manager): New class, to hold state diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 075d071..9d0f4a5 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,7 @@ +2013-10-15 Cong Hou + + * gcc.dg/vect/pr58508.c: New test. + 2013-10-14 Tobias Burnus PR fortran/58658 diff --git a/gcc/testsuite/gcc.dg/vect/pr58508.c b/gcc/testsuite/gcc.dg/vect/pr58508.c new file mode 100644 index 0000000..cb22b50 --- /dev/null +++ b/gcc/testsuite/gcc.dg/vect/pr58508.c @@ -0,0 +1,20 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -ftree-vectorize -fdump-tree-vect-details" } */ + + +/* The GCC vectorizer generates loop versioning for the following loop + since there may exist aliasing between A and B. The predicate checks + if A may alias with B across all iterations. Then for the loop in + the true body, we can assert that *B is a loop invariant so that + we can hoist the load of *B before the loop body. */ + +void foo (int* a, int* b) +{ + int i; + for (i = 0; i < 100000; ++i) + a[i] = *b + 1; +} + + +/* { dg-final { scan-tree-dump-times "hoist" 2 "vect" } } */ +/* { dg-final { cleanup-tree-dump "vect" } } */ diff --git a/gcc/tree-vect-loop-manip.c b/gcc/tree-vect-loop-manip.c index 574446a..f4fdec2 100644 --- a/gcc/tree-vect-loop-manip.c +++ b/gcc/tree-vect-loop-manip.c @@ -2477,6 +2477,92 @@ vect_loop_versioning (loop_vec_info loop_vinfo, adjust_phi_and_debug_stmts (orig_phi, e, PHI_RESULT (new_phi)); } + + /* Extract load and store statements on pointers with zero-stride + accesses. */ + if (LOOP_REQUIRES_VERSIONING_FOR_ALIAS (loop_vinfo)) + { + /* In the loop body, we iterate each statement to check if it is a load + or store. Then we check the DR_STEP of the data reference. If + DR_STEP is zero, then we will hoist the load statement to the loop + preheader, and move the store statement to the loop exit. */ + + for (gimple_stmt_iterator si = gsi_start_bb (loop->header); + !gsi_end_p (si);) + { + gimple stmt = gsi_stmt (si); + stmt_vec_info stmt_info = vinfo_for_stmt (stmt); + struct data_reference *dr = STMT_VINFO_DATA_REF (stmt_info); + + if (dr && integer_zerop (DR_STEP (dr))) + { + if (DR_IS_READ (dr)) + { + if (dump_enabled_p ()) + { + dump_printf_loc + (MSG_NOTE, vect_location, + "hoist the statement to outside of the loop "); + dump_gimple_stmt (MSG_NOTE, TDF_SLIM, stmt, 0); + dump_printf (MSG_NOTE, "\n"); + } + + gsi_remove (&si, false); + gsi_insert_on_edge_immediate (loop_preheader_edge (loop), stmt); + } + /* TODO: We also consider vectorizing loops containing zero-step + data refs as writes. For example: + + int a[N], *s; + for (i = 0; i < N; i++) + *s += a[i]; + + In this case the write to *s can be also moved after the + loop. */ + + continue; + } + else if (!dr) + { + bool hoist = true; + for (size_t i = 0; i < gimple_num_ops (stmt); i++) + { + tree op = gimple_op (stmt, i); + if (TREE_CODE (op) == INTEGER_CST + || TREE_CODE (op) == REAL_CST) + continue; + if (TREE_CODE (op) == SSA_NAME) + { + gimple def = SSA_NAME_DEF_STMT (op); + if (def == stmt + || gimple_nop_p (def) + || !flow_bb_inside_loop_p (loop, gimple_bb (def))) + continue; + } + hoist = false; + break; + } + + if (hoist) + { + gsi_remove (&si, false); + gsi_insert_on_edge_immediate (loop_preheader_edge (loop), stmt); + + if (dump_enabled_p ()) + { + dump_printf_loc + (MSG_NOTE, vect_location, + "hoist the statement to outside of the loop "); + dump_gimple_stmt (MSG_NOTE, TDF_SLIM, stmt, 0); + dump_printf (MSG_NOTE, "\n"); + } + continue; + } + } + gsi_next (&si); + } + } + /* End loop-exit-fixes after versioning. */ if (cond_expr_stmt_list)