https://gcc.gnu.org/g:309dbcea2cabb31bde1a65cdfd30bb7f87b170a2
commit r16-1645-g309dbcea2cabb31bde1a65cdfd30bb7f87b170a2 Author: Tamar Christina <tamar.christ...@arm.com> Date: Tue Jun 24 07:13:22 2025 +0100 middle-end: replace log_vf usages with vf to allow support for non-power of two vf This patch fixes a bug where the current code assumed that exact_log2 returns NULL on failure, but it instead returns -1. So there are some cases where the right shift could shift out the entire value. Secondly it also removes the requirement that VF be a power of two. With an uneven unroll factor we can easily end up with a non-power of two VF which SLP can handle. This replaces shifts with multiplication and division. The 32-bit x86 testcase from PR64110 was always wrong, it used to match by pure coincidence a vmovd inside the vector loop. What it intended to match was that the argument to the function isn't spilled and then reloaded from the stack for no reason. But on 32-bit x86 all arguments are passed on the stack anyway and so the match would have never worked. The patch seems to simplify the loop preheader which gets it to remove an intermediate zero extend which causes the match to now properly fail. As such I'm skipping the test on 32-bit x86. gcc/ChangeLog: * tree-vect-loop-manip.cc (vect_gen_vector_loop_niters, vect_gen_vector_loop_niters_mult_vf): Remove uses of log_vf. gcc/testsuite/ChangeLog: * gcc.target/i386/pr64110.c: Update testcase. Diff: --- gcc/testsuite/gcc.target/i386/pr64110.c | 2 +- gcc/tree-vect-loop-manip.cc | 36 +++++++++++++++++---------------- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/gcc/testsuite/gcc.target/i386/pr64110.c b/gcc/testsuite/gcc.target/i386/pr64110.c index 99e391916cb7..11a6929835f4 100644 --- a/gcc/testsuite/gcc.target/i386/pr64110.c +++ b/gcc/testsuite/gcc.target/i386/pr64110.c @@ -1,6 +1,6 @@ /* { dg-do compile } */ /* { dg-options "-O3 -march=core-avx2" } */ -/* { dg-final { scan-assembler "vmovd\[\\t \]" } } */ +/* { dg-final { scan-assembler "vmovd\[\\t \]" { target { ! ilp32 } } } } */ int foo (void); int a; diff --git a/gcc/tree-vect-loop-manip.cc b/gcc/tree-vect-loop-manip.cc index 20dc0e556527..469694377499 100644 --- a/gcc/tree-vect-loop-manip.cc +++ b/gcc/tree-vect-loop-manip.cc @@ -2794,7 +2794,6 @@ vect_gen_vector_loop_niters (loop_vec_info loop_vinfo, tree niters, tree niters_vector, step_vector, type = TREE_TYPE (niters); poly_uint64 vf = LOOP_VINFO_VECT_FACTOR (loop_vinfo); edge pe = loop_preheader_edge (LOOP_VINFO_LOOP (loop_vinfo)); - tree log_vf = NULL_TREE; /* If epilogue loop is required because of data accesses with gaps, we subtract one iteration from the total number of iterations here for @@ -2820,22 +2819,25 @@ vect_gen_vector_loop_niters (loop_vec_info loop_vinfo, tree niters, if (vf.is_constant (&const_vf) && !LOOP_VINFO_USING_PARTIAL_VECTORS_P (loop_vinfo)) { - /* Create: niters >> log2(vf) */ + /* Create: niters / vf, which is equivalent to niters >> log2(vf) when + vf is a power of two, and when not we approximate using a + truncating division. */ /* If it's known that niters == number of latch executions + 1 doesn't - overflow, we can generate niters >> log2(vf); otherwise we generate - (niters - vf) >> log2(vf) + 1 by using the fact that we know ratio + overflow, we can generate niters / vf; otherwise we generate + (niters - vf) / vf + 1 by using the fact that we know ratio will be at least one. */ - log_vf = build_int_cst (type, exact_log2 (const_vf)); + tree var_vf = build_int_cst (type, const_vf); if (niters_no_overflow) - niters_vector = fold_build2 (RSHIFT_EXPR, type, ni_minus_gap, log_vf); + niters_vector = fold_build2 (TRUNC_DIV_EXPR, type, ni_minus_gap, + var_vf); else niters_vector = fold_build2 (PLUS_EXPR, type, - fold_build2 (RSHIFT_EXPR, type, + fold_build2 (TRUNC_DIV_EXPR, type, fold_build2 (MINUS_EXPR, type, ni_minus_gap, - build_int_cst (type, vf)), - log_vf), + var_vf), + var_vf), build_int_cst (type, 1)); step_vector = build_one_cst (type); } @@ -2854,16 +2856,17 @@ vect_gen_vector_loop_niters (loop_vec_info loop_vinfo, tree niters, /* Peeling algorithm guarantees that vector loop bound is at least ONE, we set range information to make niters analyzer's life easier. Note the number of latch iteration value can be TYPE_MAX_VALUE so - we have to represent the vector niter TYPE_MAX_VALUE + 1 >> log_vf. */ - if (stmts != NULL && log_vf) + we have to represent the vector niter TYPE_MAX_VALUE + 1 / vf. */ + if (stmts != NULL && const_vf > 0) { if (niters_no_overflow) { int_range<1> vr (type, wi::one (TYPE_PRECISION (type)), - wi::rshift (wi::max_value (TYPE_PRECISION (type), - TYPE_SIGN (type)), - exact_log2 (const_vf), + wi::div_trunc (wi::max_value + (TYPE_PRECISION (type), + TYPE_SIGN (type)), + const_vf, TYPE_SIGN (type))); set_range_info (niters_vector, vr); } @@ -2901,13 +2904,12 @@ vect_gen_vector_loop_niters_mult_vf (loop_vec_info loop_vinfo, /* We should be using a step_vector of VF if VF is variable. */ int vf = LOOP_VINFO_VECT_FACTOR (loop_vinfo).to_constant (); tree type = TREE_TYPE (niters_vector); - tree log_vf = build_int_cst (type, exact_log2 (vf)); tree tree_vf = build_int_cst (type, vf); basic_block exit_bb = LOOP_VINFO_IV_EXIT (loop_vinfo)->dest; gcc_assert (niters_vector_mult_vf_ptr != NULL); - tree niters_vector_mult_vf = fold_build2 (LSHIFT_EXPR, type, - niters_vector, log_vf); + tree niters_vector_mult_vf = fold_build2 (MULT_EXPR, type, + niters_vector, tree_vf); /* If we've peeled a vector iteration then subtract one full vector iteration. */