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

--- Comment #8 from Martin Sebor <msebor at gcc dot gnu.org> ---
(In reply to Arnd Bergmann from comment #6)
> I figured out the qnx4 warning in the end: https://godbolt.org/z/hvqjr3

The false positive is a known problem caused by redundancy elimination (the
FRE/PRE passes) substituting one member for another when they both refer to the
same address.  It often happens with union members that share the same starting
offset but can also happen with struct members in code that refers to both the
end of one and the beginning of the next member with no intervening padding.

There's no particular reason why it picks one or the other member (maybe it
picks whichever it sees first in some order, I don't know), but the warning
only triggers when it substitutes the smaller of the two members in a larger
access.  In the strnlen test case w/o sanitization it happens to pick the
bigger member.  This can be seen in the output of -fdump-tree-pre-details:

Inserted pretmp_12 = &de_9(D)->D.2393.D.2392.dl_fname;
 in predecessor 3 (0004)
Replaced &de_9(D)->D.2393.D.2392.dl_fname with pretmp_12 in all uses of _5 =
&de_9(D)->D.2393.D.2392.dl_fname;
Replaced &de_9(D)->D.2393.D.2389.di_fname with pretmp_12 in all uses of _3 =
&de_9(D)->D.2393.D.2389.di_fname;
Removing dead stmt _3 = &de_9(D)->D.2393.D.2389.di_fname;
Removing dead stmt _5 = &de_9(D)->D.2393.D.2392.dl_fname;

The instrumentation introduced by the sanitizers changes the IL in ways that
affect the choices made by subsequent transformations.  In this case, the
sanitizer first inserts a call to __builtin___tsan_read1() to record the access
to di_fname[0].  The sanitizers run after PRE but before some FRE iterations. 
The resulting IL from the thread sanitizer looks like this:

  <bb 2> [local count: 1073741824]:
  _13 = __builtin_return_address (0);
  __builtin___tsan_func_entry (_13);
  _5 = &de_9(D)->D.2390.D.2386.di_fname[0];
  __builtin___tsan_read1 (_5);                    <<< record access to
di_fname[0]
  _1 = de_9(D)->D.2390.D.2386.di_fname[0];
  if (_1 == 0)
    goto <bb 7>; [34.00%]
  else
    goto <bb 3>; [66.00%]

  <bb 7> [local count: 365072224]:
  goto <bb 6>; [100.00%]

  <bb 3> [local count: 708669601]:
  _3 = &de_9(D)->di_status;
  __builtin___tsan_read1 (_3);
  _2 = de_9(D)->di_status;
  pretmp_12 = &de_9(D)->D.2390.D.2389.dl_fname;   <<< temporary added by PRE
  if (_2 != 0)
    goto <bb 4>; [50.00%]
  else
    goto <bb 5>; [50.00%]

  <bb 4> [local count: 354334800]:
  _4 = __builtin_strnlen (pretmp_12, 16);
  _11 = (int) _4;
  goto <bb 6>; [100.00%]

  <bb 5> [local count: 354334800]:
  _6 = __builtin_strnlen (pretmp_12, 48);
  _10 = (int) _6;

  <bb 6> [local count: 1073741824]:
  # _7 = PHI <0(7), _11(4), _10(5)>
  __builtin___tsan_func_exit ();
  return _7;

}

The IL above is then changed by FRE which replaces dl_fname with di_fname:

Value numbering stmt = pretmp_12 = &de_9(D)->D.2390.D.2389.dl_fname;
Setting value number of pretmp_12 to _5 (changed)
_5 is available for _5
Replaced &de_9(D)->D.2390.D.2389.dl_fname with _5 in all uses of pretmp_12 =
&de_9(D)->D.2390.D.2389.dl_fname;

The warning is then issued by the strlen pass that runs after FRE5 and that
sees the IL below:

  <bb 2> [local count: 1073741824]:
  _13 = __builtin_return_address (0);
  __builtin___tsan_func_entry (_13);
  _5 = &de_9(D)->D.2393.D.2389.di_fname[0];   <<< inserted by sanitizer
  __builtin___tsan_read1 (_5);                <<< record access to di_fname[0] 
  _1 = de_9(D)->D.2393.D.2389.di_fname[0];    <<< inserted by FRE5
  if (_1 == 0)
    goto <bb 6>; [34.00%]
  else
    goto <bb 3>; [66.00%]

  <bb 3> [local count: 708669601]:
  _3 = &de_9(D)->di_status;
  __builtin___tsan_read1 (_3);
  _2 = de_9(D)->di_status;
  if (_2 != 0)
    goto <bb 4>; [50.00%]
  else
    goto <bb 5>; [50.00%]

  <bb 4> [local count: 354334800]:
  _4 = strnlen (_5, 16);
  _11 = (int) _4;
  goto <bb 6>; [100.00%]

  <bb 5> [local count: 354334800]:
  _6 = strnlen (_5, 48);                      <<< -Wstringop-overread
  _10 = (int) _6;

  <bb 6> [local count: 1073741824]:
  # _7 = PHI <0(2), _11(4), _10(5)>
  __builtin___tsan_func_exit ();
  return _7;

}

I'm hoping to change PRE/FRE in GCC 12 to replace the member substitution with
one involving &object + offsetof (type, memer).  That will avoid the false
positives in these cases with no adverse impact on optimization.

Reply via email to