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

Jakub Jelinek <jakub at gcc dot gnu.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |jakub at gcc dot gnu.org,
                   |                            |law at gcc dot gnu.org,
                   |                            |redi at gcc dot gnu.org

--- Comment #1 from Jakub Jelinek <jakub at gcc dot gnu.org> ---
This is caused by the uninitialized __capacity variable in
basic_string& operator=(basic_string&& __str);

The code is like:
            // Just move the allocated pointer, our allocator can free it.
            pointer __data = nullptr;
            size_type __capacity;
            if (!_M_is_local())
              {
                if (_Alloc_traits::_S_always_equal())
                  {
                    // __str can reuse our existing storage.
                    __data = _M_data();
                    __capacity = _M_allocated_capacity;
                  }
                else // __str can't use it, so free it.
                  _M_destroy(_M_allocated_capacity);
              }

            _M_data(__str._M_data());
            _M_length(__str.length());
            _M_capacity(__str._M_allocated_capacity);
            if (__data)
              {
                __str._M_data(__data);
                __str._M_capacity(__capacity);
              }
            else
              __str._M_data(__str._M_local_buf);

Although __capacity is never actually used uninitialized (and e.g. the uninit
warning pass figures that out), because it is used only if __data is non-NULL
and __data is non-NULL only if __capacity is also initialized, with -Og or e.g.
with -O1 -fno-tree-dominator-opts, if we don't perform jump threading, we end
up with something like:
  if (&dummy.D.19123._M_local_buf != _35)
    goto <bb 14>; [70.00%]
  else
    goto <bb 15>; [30.00%]

  <bb 14> [local count: 173485181]:
  __capacity_44 = dummy.D.19123._M_allocated_capacity;

  <bb 15> [local count: 247835973]:
  # __data_47 = PHI <_35(14), 0B(13)>
  # __capacity_48 = PHI <__capacity_44(14), __capacity_50(D)(13)>
  dummy._M_dataplus._M_p = _37;
  _45 = D.24766._M_string_length;
  dummy._M_string_length = _45;
  _46 = D.24766.D.19123._M_allocated_capacity;
  dummy.D.19123._M_allocated_capacity = _46;
  if (__data_47 != 0B)
    goto <bb 16>; [70.00%]
  else
    goto <bb 17>; [30.00%]

  <bb 16> [local count: 173485181]:
  D.24766._M_dataplus._M_p = __data_47;
  D.24766.D.19123._M_allocated_capacity = __capacity_48;
  goto <bb 18>; [100.00%]

before expansion and later when IRA checks for the clobbered vars, it does:
      if (VAR_P (decl)
          && DECL_RTL_SET_P (decl)
          && REG_P (DECL_RTL (decl))
          && regno_clobbered_at_setjmp (setjmp_crosses, REGNO (DECL_RTL
(decl))))
where the last one uses:
  return ((REG_N_SETS (regno) > 1
           || REGNO_REG_SET_P (df_get_live_out (ENTRY_BLOCK_PTR_FOR_FN (cfun)),
                               regno))
          && REGNO_REG_SET_P (setjmp_crosses, regno));
While REG_N_SETS is just 1 here for the pseudo used for __capacity, because of
the uninitialized use in the PHI it is considered live on the entry block and
that is why the warning is emitted.

Changing libstdc++ code to size_type __capacity = 0; makes the warning away,
though we'd need to check if it doesn't result in worse generated code.

Reply via email to