https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128615

            Bug ID: 128615
           Summary: [14/15/16/17 Regression] wrong code at -O3: predictive
                    commoning unrolling drops the exit test of a
                    256-iteration unsigned char loop (niter + 1 wraps in
                    determine_exit_conditions)
           Product: gcc
           Version: 17.0
            Status: UNCONFIRMED
          Keywords: wrong-code
          Severity: normal
          Priority: P3
         Component: tree-optimization
          Assignee: unassigned at gcc dot gnu.org
          Reporter: lzy20000419 at outlook dot com
  Target Milestone: ---

Fails with trunk (gcc 17.0.0 20261110, 33ed38d4ee09) and with 12.1.0 through
16.2.0; 9.5.0 through 11.5.0 work.
Target: x86_64-pc-linux-gnu (target-independent).

    int b[1024], x[1024];

    __attribute__((noipa)) void
    f (int *__restrict q, const int *__restrict p)
    {
      unsigned char i = 0;
      do
        *q++ = p[0] + p[1] + p[2] + p[3];
      while (p++, i++ != 255);  /* 256 iterations */
    }

    int
    main (void)
    {
      for (int k = 0; k < 1024; k++)
        x[k] = -1;
      f (x, b);
      if (x[256] != -1)
        __builtin_abort ();
      return 0;
    }

    gcc -O3 t.c && ./a.out

Observed: abort. f stores 768 elements instead of 256. Expected: exit 0.
-O3 -fno-predictive-commoning works. -O2 -fpredictive-commoning fails the same
way. Loop unroll-and-jam and -fprefetch-loop-arrays reach the same code through
tree_unroll_loop when the unroll factor is not a power of two.

Godbolt (trunk): https://godbolt.org/z/1c1vMGx51

Cause: determine_exit_conditions (tree-ssa-loop-manip.cc) computes the
iteration count as desc->niter + 1 in the type of the control IV:

      tree niter = fold_build2 (PLUS_EXPR, type, desc->niter,
                                build_one_cst (type));
      if (multiple_of_p (type, niter, build_int_cst (type, factor)))
        return;

Here type is unsigned char and niter is 255, so niter + 1 folds to 0. 0 is a
multiple of the unroll factor 3, so the function returns without an exit bound.
tree_transform_and_unroll_loop then removes the exit tests of all copies but
the last, and the loop runs 3 * 256 times.

Possible fix: test divisibility of niter + 1 in a wider type (widest_int for
constants), so that the + 1 cannot wrap.

Related: PR127405 is a different exit-bound path in the same function.

Disclaimer: This test case was found with LLM-assisted targeted testing, and
the report was written with LLM assistance.
  • [Bug tree-optimization/128615]... lzy20000419 at outlook dot com via Gcc-bugs

Reply via email to