Bug 52078

Summary: Bogus may be used uninitialized warning
Product: gcc Reporter: Ryan Mansfield <rmansfield>
Component: tree-optimizationAssignee: Not yet assigned to anyone <unassigned>
Status: RESOLVED FIXED    
Severity: normal CC: law, manu
Priority: P3 Keywords: diagnostic
Version: 4.7.0   
Target Milestone: ---   
Host: x86_64-linux-gnu Target: x86_64-linux-gnu
Build: x86_64-linux-gnu Known to work:
Known to fail: Last reconfirmed: 2012-02-01 00:00:00
Bug Depends on:    
Bug Blocks: 24639    

Description Ryan Mansfield 2012-02-01 02:37:52 UTC
gcc version 4.7.0 20120201 (experimental) [trunk revision 183790] (GCC) 

Testcase:

#include <string.h>

int
foo (char *nname, char *oname)
{
  int cmp;

  while (nname != oname && (cmp = strcmp (nname, oname)) < 0)
    {
      break;
    }
  if (nname == oname || cmp == 0)
    {
      return 1;
    }
  else
    {
      return 0;
    }
}


$ ./xgcc -B. -O2 -Wall ~/init.c -c 
/home/ryan/init.c: In function 'foo':
/home/ryan/init.c:12:29: warning: 'cmp' may be used uninitialized in this function [-Wmaybe-uninitialized]

Since both && and || operators guarantee left-to-right evaluation, in the case where nname == oname, strcmp is never called but in the (name == oname || cmp == 0) expression, nname == oname and the second operand is not evaluated so cmp is never used uninitialized.
Comment 1 Manuel López-Ibáñez 2012-02-01 08:17:27 UTC
I haven't followed development for a while, but I don't think GCC uninit analysis is powerful enough to detect this or even simpler cases like PR36550 and PR20968.
Comment 2 Richard Biener 2012-02-01 09:28:49 UTC
Yeah, it's simply too confusing code for GCCs static analysis.  Thus the "may be used uninitialized".
Comment 3 Jeffrey A. Law 2017-03-31 18:12:25 UTC
If we look at the VRP dump after ASSERT_EXPR insertion it's obvious what needs to happen here:

;   basic block 2, loop depth 0, count 0, freq 10000, maybe hot
;;    prev block 0, next block 7, flags: (NEW, REACHABLE, VISITED)
;;    pred:       ENTRY [100.0%]  (FALLTHRU,EXECUTABLE)
  if (nname_6(D) != oname_7(D))
    goto <bb 3>; [70.00%]
  else
    goto <bb 7>; [30.00%]
;;    succ:       3 [70.0%]  (TRUE_VALUE,EXECUTABLE)
;;                7 [30.0%]  (FALSE_VALUE,EXECUTABLE)

;;   basic block 7, loop depth 0, count 0, freq 3000, maybe hot
;;    prev block 2, next block 3, flags: (NEW)
;;    pred:       2 [30.0%]  (FALSE_VALUE,EXECUTABLE)
  nname_12 = ASSERT_EXPR <nname_6(D), nname_6(D) == oname_7(D)>;
  oname_15 = ASSERT_EXPR <oname_7(D), oname_7(D) == nname_12>;
  goto <bb 4>; [100.00%]
;;    succ:       4 [100.0%]  (FALLTHRU)

;;   basic block 3, loop depth 0, count 0, freq 7000, maybe hot
;;    prev block 7, next block 4, flags: (NEW, REACHABLE, VISITED)
;;    pred:       2 [70.0%]  (TRUE_VALUE,EXECUTABLE)
  nname_11 = ASSERT_EXPR <nname_6(D), nname_6(D) != oname_7(D)>;
  oname_14 = ASSERT_EXPR <oname_7(D), oname_7(D) != nname_11>;
  _10 = __builtin_strcmp (nname_11, oname_14);
  oname_16 = ASSERT_EXPR <oname_14, oname_14 != 0B>;
  nname_13 = ASSERT_EXPR <nname_11, nname_11 != 0B>;
;;    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)
;;                7 [100.0%]  (FALLTHRU)
  # cmp_4 = PHI <_10(3), cmp_8(D)(7)>
  _1 = nname_6(D) == oname_7(D);
  _2 = cmp_4 == 0;
  _3 = _1 | _2;
  if (_3 != 0)
    goto <bb 6>; [46.00%]
  else
    goto <bb 5>; [54.00%]
;;    succ:       6 [46.0%]  (TRUE_VALUE,EXECUTABLE)
;;                5 [54.0%]  (FALSE_VALUE,EXECUTABLE)

If we traverse the edge 2->7, then we know that nname == oname (which is shown in the IL via the ASSERT_EXPRs in BB7).    We can use that to simplify the computation of _3 in BB4 and ultimately thread 2->7->6 and eliminate the test of cmp_4 on the threaded path (it becomes dead).

That eliminates the uninitialized use.

This is fixed on the trunk, most likely due to the work for pr71437 which looks to exploit ASSERT_EXPRs more aggressively.