On 9/24/20 3:43 AM, Richard Biener wrote:
On Wed, 23 Sep 2020, Jason Merrill wrote:

On 9/23/20 2:42 PM, Richard Biener wrote:
On September 23, 2020 7:53:18 PM GMT+02:00, Jason Merrill <ja...@redhat.com>
wrote:
On 9/23/20 4:14 AM, Richard Biener wrote:
C++ operator delete, when DECL_IS_REPLACEABLE_OPERATOR_DELETE_P,
does not cause the deleted object to be escaped.  It also has no
other interesting side-effects for PTA so skip it like we do
for BUILT_IN_FREE.

Hmm, this is true of the default implementation, but since the function

is replaceable, we don't know what a user definition might do with the
pointer.

But can the object still be 'used' after delete? Can delete fail / throw?

What guarantee does the predicate give us?

The deallocation function is called as part of a delete expression in order to
release the storage for an object, ending its lifetime (if it was not ended by
a destructor), so no, the object can't be used afterward.

OK, but the delete operator can access the object contents if there
wasn't a destructor ...

A deallocation function that throws has undefined behavior.

OK, so it seems the 'replaceable' operators are the global ones
(for user-defined/class-specific placement variants I see arbitrary
extra arguments that we'd possibly need to handle).

I'm happy to revert but I'd like to have a testcase that FAILs
with the patch ;)

Now, the following aborts:

struct X {
   static struct X saved;
   int *p;
   X() { __builtin_memcpy (this, &saved, sizeof (X)); }
};
void operator delete (void *p)
{
   __builtin_memcpy (&X::saved, p, sizeof (X));
}
int main()
{
   int y = 1;
   X *p = new X;
   p->p = &y;
   delete p;
   X *q = new X;
   *(q->p) = 2;
   if (y != 2)
     __builtin_abort ();
}

and I could fix this by not making *p but what *p points to escape.
The testcase is of course maximally awkward, but hey ... ;)

Now this would all be moot if operator delete may not access
the object (or if the object contents are undefined at that point).

Oh, and the testcase segfaults when compiled with GCC 10 because
there we elide the new X / delete p pair ... which is invalid then?
Hmm, we emit

   MEM[(struct X *)_8] ={v} {CLOBBER};
   operator delete (_8, 8);

so the object contents are undefined _before_ calling delete
even when I do not have a DTOR?  That is, the above,
w/o -fno-lifetime-dse, makes the PTA patch OK for the testcase.

Yes, all classes have a destructor, even if it's trivial, so the object's lifetime definitely ends before the call to operator delete. This is less clear for scalar objects, but treating them similarly would be consistent with other recent changes, so I think it's fine for us to assume that scalar objects are also invalidated before the call to operator delete. But of course this doesn't apply to explicit calls to operator delete outside of a delete expression.

Richard.

Bootstrapped and tested on x86_64-unknown-linux-gnu, pushed.

Richard.

2020-09-23  Richard Biener  <rguent...@suse.de>

  PR tree-optimization/97151
  * tree-ssa-structalias.c (find_func_aliases_for_call):
  DECL_IS_REPLACEABLE_OPERATOR_DELETE_P has no effect on
  arguments.

        * g++.dg/cpp1y/new1.C: Adjust for two more handled transforms.
---
    gcc/testsuite/g++.dg/cpp1y/new1.C | 4 ++--
    gcc/tree-ssa-structalias.c        | 2 ++
    2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/gcc/testsuite/g++.dg/cpp1y/new1.C
b/gcc/testsuite/g++.dg/cpp1y/new1.C
index aa5f647d535..fec0088cb40 100644
--- a/gcc/testsuite/g++.dg/cpp1y/new1.C
+++ b/gcc/testsuite/g++.dg/cpp1y/new1.C
@@ -69,5 +69,5 @@ test_unused() {
      delete p;
    }
-/* { dg-final { scan-tree-dump-times "Deleting : operator delete" 5
"cddce1"} } */
-/* { dg-final { scan-tree-dump-times "Deleting : _\\d+ = operator
new" 7 "cddce1"} } */
+/* { dg-final { scan-tree-dump-times "Deleting : operator delete" 6
"cddce1"} } */
+/* { dg-final { scan-tree-dump-times "Deleting : _\\d+ = operator
new" 8 "cddce1"} } */
diff --git a/gcc/tree-ssa-structalias.c b/gcc/tree-ssa-structalias.c
index 44fe52e0f65..f676bf91e95 100644
--- a/gcc/tree-ssa-structalias.c
+++ b/gcc/tree-ssa-structalias.c
@@ -4857,6 +4857,8 @@ find_func_aliases_for_call (struct function
*fn, gcall *t)
      point for reachable memory of their arguments.  */
           else if (flags & (ECF_PURE|ECF_LOOPING_CONST_OR_PURE))
        handle_pure_call (t, &rhsc);
+      else if (fndecl && DECL_IS_REPLACEABLE_OPERATOR_DELETE_P
(fndecl))
+       ;
           else
     handle_rhs_call (t, &rhsc);
          if (gimple_call_lhs (t))






Reply via email to