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. */