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

--- Comment #3 from Richard Biener <rguenth at gcc dot gnu.org> ---
  Root Cause
  The loop unrolling framework in tree-ssa-loop-manip.cc handles NE_EXPR loop
  conditions by converting them to directional inequalities (LT_EXPR or
GT_EXPR)
  to prevent overshooting under unrolled loop body decrements/increments. 

  However, when an unsigned loop decrements towards a boundary (like index !=
  static_cast<size_t>(-1) where the boundary value -1 wraps to max), the
  unroller:
   1. Converted NE_EXPR to GT_EXPR (due to negative step).
   2. Generated an unrolled loop comparison check of ctr_after > 0 (where 0 is
      the exit bound calculated relative to the max wrap-around boundary).
   3. Under -O3, during Predictive Commoning with an unroll factor of 2, the
      counter was decremented by 2 on each iteration.
   4. When ctr was 1, ctr_after = 1 - 2 underflowed to max, which is still
      strictly > 0 in unsigned arithmetic.
   5. Consequently, the exit check ctr_after > 0 evaluated to true, bypassing
      loop termination entirely and leading to an infinite loop (which ran
until
      memory underflow caused a segmentation fault or generated incorrect
      values).

reduced testcase (does not fail on trunk either):

/* { dg-do run } */
/* { dg-options "-O3" } */

extern void abort (void);

void __attribute__((noipa)) test(unsigned long n, double *a, double *b) {
    for (unsigned long i = n - 2; i != (unsigned long)-1; --i) {
        a[i] = b[i] - a[i+1] - a[i+2];
    }
}

int main() {
    /* Allocate larger size of 6 to prevent any out-of-bounds accesses. */
    double a[6] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
    double b[6] = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0};

    /* n = 4, so loop starting index is 2.
       Expected trace of correct execution:
       i = 2: a[2] = b[2] - a[3] - a[4] = 3.0 - 0.0 - 0.0 = 3.0
       i = 1: a[1] = b[1] - a[2] - a[3] = 2.0 - 3.0 - 0.0 = -1.0
       i = 0: a[0] = b[0] - a[1] - a[2] = 1.0 - (-1.0) - 3.0 = -1.0
    */
    test(4, a, b);

    if (a[2] != 3.0 || a[1] != -1.0 || a[0] != -1.0) {
        abort ();
    }
    return 0;
}

Reply via email to