Bug 68548 - bogus "may be used uninitialized" (predicate analysis)
Summary: bogus "may be used uninitialized" (predicate analysis)
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: tree-optimization (show other bugs)
Version: 6.0
: P3 normal
Target Milestone: 14.0
Assignee: Not yet assigned to anyone
URL:
Keywords: diagnostic, missed-optimization
Depends on:
Blocks: jumpthreading Wuninitialized
  Show dependency treegraph
 
Reported: 2015-11-25 23:18 UTC by Matt Turner
Modified: 2024-03-16 23:48 UTC (History)
4 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail: 10.2.0, 11.0, 4.9.4, 5.5.0, 6.4.0, 7.2.0, 8.3.0, 9.1.0
Last reconfirmed: 2023-07-11 00:00:00


Attachments
u.c (157 bytes, text/plain)
2015-11-25 23:18 UTC, Matt Turner
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Matt Turner 2015-11-25 23:18:51 UTC
Created attachment 36842 [details]
u.c

gcc -Wmaybe-uninitialized wrongly warns about data0 being uninitialized in the attached test case.

This may be a duplicate of https://gcc.gnu.org/bugzilla/show_bug.cgi?id=42145
Comment 1 Manuel López-Ibáñez 2015-11-26 01:32:05 UTC
(In reply to Matt Turner from comment #0)
> Created attachment 36842 [details]
> u.c
> 
> gcc -Wmaybe-uninitialized wrongly warns about data0 being uninitialized in
> the attached test case.
> 
> This may be a duplicate of https://gcc.gnu.org/bugzilla/show_bug.cgi?id=42145

No, it is not. This is just that the uninit predicate analysis is not powerful enough. It sees that the DEF is guarded by writemask_3(D) & 1, but it sees that the use is guarded by remaining_8 == 0, and it doesn't seem to analyse further than that.
Comment 2 Jeffrey A. Law 2016-11-22 23:32:42 UTC
True that tree-ssa-uninit.c is now powerful enough to grok this case.

But my first preference for any kind of Wuninitialized false positive is to first think of it as a missed optimization.  Fix the missed optimization and the false positive goes away for free.

In this particular case there's a couple potential approaches.

;;   basic block 2, loop depth 0, count 0, freq 10000, maybe hot
;;    prev block 0, next block 3, flags: (NEW, REACHABLE, VISITED)
;;    pred:       ENTRY [100.0%]  (FALLTHRU,EXECUTABLE)
  _1 = writemask_5(D) & 1;
  if (_1 != 0)
    goto <bb 4>;
  else
    goto <bb 3>;
;;    succ:       4 [50.0%]  (TRUE_VALUE,EXECUTABLE)
;;                3 [50.0%]  (FALSE_VALUE,EXECUTABLE)

;;   basic block 3, loop depth 0, count 0, freq 5000, maybe hot
;;    prev block 2, next block 4, flags: (NEW, VISITED)
;;    pred:       2 [50.0%]  (FALSE_VALUE,EXECUTABLE)
;;    succ:       4 [100.0%]  (FALLTHRU,EXECUTABLE)

;;   basic block 4, loop depth 0, count 0, freq 10000, maybe hot
;;    prev block 3, next block 5, flags: (NEW, REACHABLE, VISITED)
;;    pred:       3 [100.0%]  (FALLTHRU,EXECUTABLE)
;;                2 [50.0%]  (TRUE_VALUE,EXECUTABLE)
  # data0_3 = PHI <data0_6(D)(3), data_7(D)(2)>
  _2 = ~writemask_5(D);
  remaining_8 = _2 & 1;
  if (remaining_8 == 0)
    goto <bb 5>;
  else
    goto <bb 6>;

One would be to realize that remaining_8 can be expressed in terms of _1.  If that's done we expose the redundant test and we end up with the only path to bar() being the one where data0 is initialized.  This can be modeled in DOM and is about ~20 lines of code.  Not sure how often it'd trigger in the real world.

Another would be to utilize bitwise inference in jump threading in conjunction with the ASSERT_EXPRs from VRP.  That essentially allows us to construct jump threads 2->3->4->6 and 2->4->5 which result in the only path to bar() being one were data0 is initialized.  This is probably more effective, but requires extending the VRP related jump threading bits whereas I'd rather be relying upon those less.  But it might be the most tractable for gcc-7.

A third approach would be to extend the backward jump threader to walk through some binary and unary ops.  I've got some prototype code to do that, but it's not gcc-7 material.

A fourth approach would be to take the bitwise inference stuff and do it in the DOM threader instead (which will likely last longer than the VRP threader).  My worry in this case is that we won't have the ASSERT_EXPRs or range information on the appropriate edges that would allow us to identify the jump threads.

A fifth approach would be to extend tree-ssa-uninit.c.  But I'd really prefer to explore opportunities to remove the useless test and unexecutable paths through the CFG first.
Comment 3 Martin Sebor 2021-03-31 20:37:04 UTC
Reconfirmed with GCC 11.  It's not a regression.  With a patch I'm working with GCC issues the following warning and notes confirming the analysis from comment #1 and #2.

pr68548.c: In function ‘foo’:
pr68548.c:14:17: warning: ‘data0’ may be used uninitialized in this function [-Wmaybe-uninitialized]
   14 |                 bar(data0);
      |                 ^~~~~~~~~~
pr68548.c:7:18: note: when ‘(writemask & 1) == 0’
    7 |         unsigned data0;
      |                  ^~~~~
pr68548.c:7:18: note: used when ‘(writemask & 1) == 0’
pr68548.c:7:18: note: ‘data0’ was declared here

EVRP knows to fold the test for data0 != data to false but nothing substitutes data for data0 when the value is passed to another function or returned.  So the following, for instance, doesn't trigger a warning because the data0 - data subtraction is folded into zero.

int x;

void baz (unsigned writemask, unsigned data) {
    unsigned remaining = X;
    unsigned data0;

    if (writemask & X) data0 = data;

    remaining &= ~writemask;
    if (remaining == 0)
      x = data0 - data;
}
Comment 4 Andrew Pinski 2024-03-16 23:48:17 UTC
Fixed on the trunk. A jump threading improvement fixed it.