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.