Bug 85398 - g++ reports "array subscript is above array bounds" when it cannot be sure
Summary: g++ reports "array subscript is above array bounds" when it cannot be sure
Status: NEW
Alias: None
Product: gcc
Classification: Unclassified
Component: tree-optimization (show other bugs)
Version: 6.4.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords: diagnostic, missed-optimization
Depends on:
Blocks: Warray-bounds
  Show dependency treegraph
 
Reported: 2018-04-13 19:36 UTC by Thomas De Schampheleire
Modified: 2021-08-01 21:17 UTC (History)
1 user (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2021-08-01 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Thomas De Schampheleire 2018-04-13 19:36:42 UTC
In following test program:


------------------------------
#define NB_DEV 1
extern unsigned int max;

unsigned long left[NB_DEV];
unsigned long right[NB_DEV];

void foo()
{
    unsigned int i;

    for (i=1; i < max; i++)
      left[i] = right[i-1];
}
------------------------------

compiled with:

$(CXX) -Werror -Wall -O2 -c reprod.cc

g++ gives following warning/error:

reprod.cc: In function 'void foo()':
reprod.cc:13:13: error: array subscript is above array bounds [-Werror=array-bounds]
       left[i] = right[i-1];
       ~~~~~~^
cc1plus: all warnings being treated as errors
make: *** [Makefile:4: all] Error 1


While there _could_ be an array overflow, g++ cannot know for sure because the loop boundary 'max' is an external variable. The code is perfectly fine in case max == 1. In that case, the loop does nothing.

This is a reduced version of real code where the arrays left and right are dimensioned to some maximum value NB_DEV, and 'max' will be at most that NB_DEV but possibly smaller. We are thus sure there will not be an array overflow.


Going back to the reproduction code above, if you change NB_DEV to 2 (for example), no warning is thrown, even though there could still be an overflow in case max == 5, for example.

According to me, no warning should be thrown because g++ cannot surely say there is a problem.

Same problem is seen if you compile this as C code rather than C++.
Problem is not seen with -O1, only with -O2 or -O3.

This problem was tested with gcc 6.4.0 (x86_64), gcc 6.3.0 (armeb), gcc 5.4.0 (armeb) and gcc 4.9.4 (armeb).
Comment 1 Martin Sebor 2018-04-13 21:24:50 UTC
Confirmed.  It doesn't look like the assignment to left[i] affects the determination of the maximum number of iterations of the loop during unrolling.  This is also a missed optimization opportunity since the whole loop could be eliminated.
Comment 2 Richard Biener 2018-04-18 09:44:43 UTC
This is the simple case of GCC optimizing the access to a constant:

  <bb 2> [50.00%]:
  max.0_11 = max;
  if (max.0_11 > 1)
    goto <bb 3>; [50.00%]
  else
    goto <bb 4>; [50.00%]

  <bb 3> [25.00%]:
  _13 = right[0];
  left[1] = _13;

  <bb 4> [50.00%]:
  return;

and we warn for the case of max > 1.

Similarly we warn for

int bar () { return left[2]; }

even if we can't prove that bar() is actually executed.

We could change the warning to have a "may be above array bounds" form
for your case but that wouldn't handle the bar() case.
Comment 3 Thomas De Schampheleire 2018-04-18 10:01:47 UTC
(In reply to Richard Biener from comment #2)
> 
> We could change the warning to have a "may be above array bounds" form
> for your case but that wouldn't handle the bar() case.

The problem with giving warnings about potential-but-not-definite issues is that projects that compile with '-Wall -Werror' assume zero warnings to guard quality.

But if some warnings are false-positives, this strategy no longer works. The project will fail to compile even though it is perfectly fine.

You'd need a way to tell gcc that this code is fine, or put such cases in a separate warning category that is not included in Wall or can be disabled explicitly.
Comment 4 Martin Sebor 2018-04-18 15:17:23 UTC
A simpler test case is this (which is analogous to what the loop is transformed into):

$ cat x.c && gcc -O2 -S -Wall x.c
  unsigned left[1];
  unsigned long right[1];

  void f (unsigned i)
  {
    if (i)
      left[i] = right[i - 1];
  }

x.c: In function ‘f’:
x.c:7:11: warning: array subscript [0, 0] is outside array bounds of ‘unsigned int[1]’ [-Warray-bounds]
       left[i] = right[i - 1];
       ~~~~^~~

Here it's even more obvious that the warning is wrong.

It seems to me that the whole if statement could either be eliminated or its body replaced by a trap because the assignment in it is undefined.  That would eliminate the loop (and with it also the warning).