public inbox for gcc-bugs@sourceware.org
help / color / mirror / Atom feed
From: "rguenth at gcc dot gnu.org" <gcc-bugzilla@gcc.gnu.org>
To: gcc-bugs@gcc.gnu.org
Subject: [Bug libstdc++/109442] Dead local copy of std::vector not removed from function
Date: Wed, 12 Apr 2023 09:45:25 +0000	[thread overview]
Message-ID: <bug-109442-4-6Qljat9YRR@http.gcc.gnu.org/bugzilla/> (raw)
In-Reply-To: <bug-109442-4@http.gcc.gnu.org/bugzilla/>

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

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

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|RESOLVED                    |REOPENED
                 CC|                            |jason at gcc dot gnu.org
         Resolution|WONTFIX                     |---

--- Comment #6 from Richard Biener <rguenth at gcc dot gnu.org> ---
(In reply to Jonathan Wakely from comment #5)
> (In reply to Richard Biener from comment #4)
> > (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.
> 
> But if the library did _37->~_Tp() to destroy the element at _37 then it
> would be dead, and accessing the element outside its lifetime would be
> undefined. The bytes can only be accessed as char, unsigned char or
> std::byte after that.
> 
> > IIRC 'operator delete' (_ZdlPvm in this case), can be
> > overridden by the user and can inspect the memory state before "releasing"
> > the storage?
> 
> That would be insane for operator delete to do that. Maybe possible, but
> insane.

Well, the standard doesn't prohibit "insanity" so we have to generate
correct code even in those cases, no?  After all this is what PR101480
was about ("insane" stuff) done even to new/delete _expressions_.

Would it be correct (by the standards wording) to extend the current
handling of new/delete expressions - which is that 'new' returns a pointer
not aliasing to any other pointer and that 'delete' does not read from
the memory pointed to by the first argument and this pointer also does not
escape to direct calls of operator new/delete?

Basically in gimple.cc:gimple_call_fnspec we currenty do

  /* If the call is to a replaceable operator delete and results
     from a delete expression as opposed to a direct call to
     such operator, then we can treat it as free.  */
  if (fndecl
      && DECL_IS_OPERATOR_DELETE_P (fndecl)
      && DECL_IS_REPLACEABLE_OPERATOR (fndecl)
      && gimple_call_from_new_or_delete (stmt))
    return ". o ";
  /* Similarly operator new can be treated as malloc.  */
  if (fndecl
      && DECL_IS_REPLACEABLE_OPERATOR_NEW_P (fndecl)
      && gimple_call_from_new_or_delete (stmt))
    return "m ";

where the comments do not exactly match the behavior (we also allow
arbitrary side-effects on global memory).  That we model 'delete'
as possibly writing to the released storage is so it serves as barrier for
earlier loads/stores to avoid sinking them across.

> In any case, the stores to _37 would be dead now, right? So even if operator
> delete inspects the memory as raw bytes using memcmp or similar, it's
> reading uninitialized storage, so any value is acceptable. So the stores to
> _37 should be DSE-able now.

There's noting in the IL indicating that though (but I haven't updated
libstd++ a while to create a updated preprocessed source - will do so now).

> > 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;
> 
> Yes, that's correct. Using a delete expression here would be completely
> wrong.

I see.  That's unfortunate (we'll never be able to elide the actual
allocation then).

> > (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;
> 
> That would make std::vector incorrect though.
> 
> > 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.
> 
> Then there's no bug. std::vector is correct, and using a delete[] expression
> would be a bug.

OK, then how do we arrange things so the compiler knows it can completely
elide the unused std::vector?  Or do you say it's not valid to do that?

  parent reply	other threads:[~2023-04-12  9:45 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-04-06 19:00 [Bug tree-optimization/109442] New: " hiraditya at msn dot com
2023-04-11 13:28 ` [Bug tree-optimization/109442] " rguenth at gcc dot gnu.org
2023-04-11 13:33 ` redi at gcc dot gnu.org
2023-04-11 13:38 ` redi at gcc dot gnu.org
2023-04-12  7:40 ` [Bug libstdc++/109442] " rguenth at gcc dot gnu.org
2023-04-12  9:29 ` redi at gcc dot gnu.org
2023-04-12  9:45 ` rguenth at gcc dot gnu.org [this message]
2023-04-12 10:01 ` redi at gcc dot gnu.org
2023-04-12 10:13 ` redi at gcc dot gnu.org
2023-04-12 10:22 ` rguenth at gcc dot gnu.org
2023-04-12 10:29 ` redi at gcc dot gnu.org
2023-04-12 10:35 ` redi at gcc dot gnu.org
2023-04-12 11:52 ` rguenther at suse dot de
2023-04-12 11:55 ` redi at gcc dot gnu.org
2023-04-16 18:38 ` richard-gccbugzilla at metafoo dot co.uk
2023-04-17  6:49 ` rguenth at gcc dot gnu.org
2023-04-18  2:15 ` richard-gccbugzilla at metafoo dot co.uk
2023-06-15 18:38 ` hiraditya at msn dot com
2024-05-11  0:07 ` xry111 at gcc dot gnu.org
2024-05-11 16:05 ` hubicka at gcc dot gnu.org
2024-05-14 13:47   ` Jan Hubicka
2024-05-11 16:44 ` redi at gcc dot gnu.org
2024-05-14 13:47 ` hubicka at ucw dot cz

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=bug-109442-4-6Qljat9YRR@http.gcc.gnu.org/bugzilla/ \
    --to=gcc-bugzilla@gcc.gnu.org \
    --cc=gcc-bugs@gcc.gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).