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

Richard Biener <rguenth at gcc dot gnu.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
          Component|tree-optimization           |libstdc++

--- Comment #4 from Richard Biener <rguenth at gcc dot gnu.org> ---
(In reply to Jonathan Wakely from comment #3)
> Ah, maybe the problem is that the library code manually elides destroying
> the elements, precisely because it's a no-op. So we don't actually destroy
> the elements, which means the compiler might think they're still initialized
> and so could be inspected.
> 
> If the library explicitly does vec[i].~T() for every i then would that help?
> The compiler would know there are no valid elements in the storage, and so
> nothing operator delete could inspect.
> 
> We could continue to elide destroying the elements when !defined
> __OPTIMIZE__ so that we don't run a loop that does nothing, but with
> optimization enabled rely on the compiler to remove that loop.

I don't think that would help.  The issue is the compiler thinks that

operator delete (_37, _49);

uses the memory at _37 and thus the stores

*_37 = _24;

and

__builtin_memmove (_37, pretmp_63, _23);

are not dead.  IIRC 'operator delete' (_ZdlPvm in this case), can be
overridden by the user and can inspect the memory state before "releasing"
the storage?

This also seems to be a form not handled by fndecl_dealloc_argno
even though it's marked as DECL_IS_OPERATOR_DELETE_P and
DECL_IS_REPLACEABLE_OPERATOR - but the actual call stmt is not marked
as such.  That's to catch a new/delete _expression_ and not a direct
call to the operator - ISTR we need the semantics guaranteed by the
standard for new/delete expressions here.

I see ~_Vector_base uses

 typedef __gnu_cxx::__alloc_traits<_Tp_alloc_type> _Tr;
 if (__p)
   _Tr::deallocate(_M_impl, __p, __n);

but I fail to trace that further (in the preprocessed source), the
line info on the delete stmt above points to new_allocator.h:168
which is

        _GLIBCXX_OPERATOR_DELETE(_GLIBCXX_SIZED_DEALLOC(__p, __n));

which looks like a direct invocation of operator delete rather than
a delete expression.  So the compiler rightfully(?) refuses to apply
strict semantics ('delete' is _just_ like free with no other side-effects,
a 'new' / 'delete' pair may be elided).

Indeed in preprocessed source the above expands to

 ::operator delete((__p), (__n) * sizeof(_Tp));

rather than

  delete[] __p;

(or what the correct syntax with explicit size would be).  In theory
we could implement an attribute specifying a operator new or delete
invocation acts like a new or delete expression and use that in the
library and make sure that CALL_FROM_NEW_OR_DELETE_P is set on the
generated CALL_EXPRs.

When I replace the above operator invocation in the library with

  delete[] (char *)__p;

then the dead stores are elided but since I didn't track down the call
to 'operator new' which suffers from a similar problem the new/delete
pair isn't elided yet.

So in the end it seems this is a library/C++ frontend issue.

Reply via email to