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

Richard Biener <rguenth at gcc dot gnu.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|NEW                         |ASSIGNED
           Assignee|unassigned at gcc dot gnu.org      |rguenth at gcc dot 
gnu.org
           Priority|P3                          |P2

--- Comment #6 from Richard Biener <rguenth at gcc dot gnu.org> ---
Reduced testcase:

int bf;

__attribute__((noipa))
int get_bf () { return 5; }

__attribute__((noipa))
void test (int x)
{
  int cb = -996736171;
  if (x == 0)
    cb = 0;
  bf = get_bf ();
  while (1)
    {
      bf -= 2;
      if (-520093677 * bf >= cb)
        break;
    }
}

int main ()
{
  test (1);
  if (bf != 1)
    __builtin_abort ();
  return 0;
}

the issue is

   1. Niter Analysis & Assumptions:
     In tree-ssa-loop-niter.cc:number_of_iterations_lt, when the loop
  termination condition cannot be proven unconditionally without potential
  overflow of the difference between bounds, an assumption is generated (e.g.
  iv0->base >= MIN + diff) in niter->assumptions. Under that assumption,
  iv0->base - diff does not underflow, and may_be_zero is computed as iv0->base
  - diff > iv1->base.
     As documented in tree-ssa-loop-niter.cc:1809:

   1    /* The meaning of these assumptions is this:
   2       if !assumptions
   3         then the rest of information does not have to be valid
   4       if may_be_zero then the loop does not roll, even if
   5         niter != 0.  */
     Thus, may_be_zero is only valid under the condition that
niter->assumptions
  holds.

   2. Unsound Combination in tree-vect-loop.cc:
     In vect_get_loop_niters (gcc/tree-vect-loop.cc), if niter_assumptions was
  non-null and not constant true, it attempted to simplify the loop's niter
  expression by folding !may_be_zero directly into niter_assumptions:

   1    if (niter_assumptions && !integer_nonzerop (niter_assumptions))
   2      niter_assumptions = fold_build2 (TRUTH_AND_EXPR, boolean_type_node,
   3                                       niter_assumptions,
   4                                       fold_build1 (TRUTH_NOT_EXPR,
   5                                                    boolean_type_node,
   6                                                    may_be_zero));
     This combined expression was placed into LOOP_VINFO_NITERS_ASSUMPTIONS and
  emitted via force_gimple_operand_1 during vect_loop_versioning.

   3. GIMPLE Expansion & Undefined Behavior:
     In GIMPLE, TRUTH_AND_EXPR does not short-circuit when lowered into a
  condition statement list; it lowered to & in the same basic block:

   1    _321 = _328 >= -1107296295;
   2    _318 = _328 + -1040187353;
   3    _317 = cb_130 >= _318;
   4    _316 = _317 & _321;
     When _328 < -1107296295 (when _321 is false), the signed addition _318 =
  _328 + -1040187353 executes unconditionally and overflows signed 32-bit int,
  invoking undefined behavior.

   4. Miscompilation in Ranger and Jump Threading:
     Because signed overflow is undefined (-fstrict-overflow), Ranger assumed
  _318 = _328 + -1040187353 did not overflow and deduced _328 must be
[43451183,
  +INF] on the false edge. Since cb_130 <= 0, Ranger deduced cb_130 <= _328 was
  always true on the path to the scalar loop, causing pass_thread_jumps
  (backward jump threading) to thread the false edge directly to the loop exit
  with the wrong value (bf = 3 instead of 1), eliminating the scalar loop.


I need to verify the above analysis.  The suggested fix is to elide

    7 -          /* Try to combine may_be_zero with assumptions, this can
simplify
    8 -         computation of niter expression.  */

Reply via email to