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

--- Comment #8 from Jakub Jelinek <jakub at gcc dot gnu.org> ---
So, to me this doesn't look like a recent reassoc1 regression, but rather
something that been there since
~ r0-119920-gd578e863b08e64789f767c95f85acd10dc477bdf

The inter-bb range optimization pass uses no_side_effect_bb as a test for the
bbs in between, which makes sure that no statements have side-effects, there
are only gimple assignments except for the last GIMPLE_COND and those
statements can't trap and their lhs is consumed within the same bb.

  if (a.1_1 >= 0)
    goto <bb 5>; [59.00%]
  else
    goto <bb 4>; [41.00%]

  <bb 4> [local count: 440234144]:
  _3 = -2147483647 - a.1_1;
  _9 = a.1_1 != -2147479551;
  _4 = _3 == 1;
  _8 = _4 | _9;
  if (_8 != 0)
    goto <bb 5>; [34.51%]
  else
    goto <bb 3>; [65.49%]

is then treated as if ((a.1_1 >= 0) | (-2147483647 - a.1_1 == 1) | (a.1_1 !=
-2147479551)) goto bb5; else goto bb3;
and in there we optimize from there the (a.1_1 >= 0) | (a.1_1 != -2147479551)
part of it into (a.1_1 != -2147479551).  That is all fine, but we have chosen
to put the merged condition into the place of the old _9 test and make the
the a.1_1 >= 0 test (which has been replaced by false).  Usually that doesn't
really matter where we stick it, when merging multiple "ops" into one, one
simply wins (that is the range in update_range_test) and the other(s) are
marked
as true/false (otherrange ... otherrange + count - 1).
The problem above is that the _3 = -2147483647 - a.1_1; stmt is done in
undefined overflow type and thus can trigger UB where there wasn't one before
for certain values.

One possibility to fix this would be to punt on statements like that in
no_side_effect_bb (e.g. the normal way to encode a range is using unsigned
arithmetics).  But I'd be afraid we could easily regress on what the
optimization can handle.

Another possibility is try to be smarter in update_range_test.  Instead of
always picking range->idx as the winner and otherrange*->idx as the stmts
replaced by false or true, perhaps we should try to be smarter, go through them
all first and pick the one from the first bb (one dominating all the other ops)
and if the merge is successful, swap range->idx with the most dominating
otherrange->idx.

So, instead of the problematic
  <bb 2> [local count: 118111598]:

  <bb 3> [local count: 1073741824]:
  a.1_1 = a;
  _3 = -2147483647 - a.1_1;
  _10 = a.1_1 != -2147479551;
  _9 = a.1_1 != -2147479551;
  _4 = _3 == 1;
  _2 = _4 | _10;
  if (_2 != 0)
    goto <bb 4>; [34.51%]
  else
    goto <bb 3>; [65.49%]
we'd get
  a.1_1 = a;
  if (a.1_1 != -2147479551)
    goto <bb 5>; [59.00%]
  else
    goto <bb 4>; [41.00%]

  <bb 4> [local count: 440234144]:
  _3 = -2147483647 - a.1_1;
  _9 = 0;
  _4 = _3 == 1;
  _8 = _4 | _9;
  if (_8 != 0)
    goto <bb 5>; [34.51%]
  else
    goto <bb 3>; [65.49%]


Another question but that is GCC 13 material is why we don't optimize
int
foo (int x)
{
  return -2147483647 - x <= 0;
}
into:
  return x >= -2147483647
during GIMPLE (we optimize it during expansion it seems).

Reply via email to