https://gcc.gnu.org/g:080af1a675d94dc25d248b2638e82c03e1476c01
commit r17-2385-g080af1a675d94dc25d248b2638e82c03e1476c01 Author: Richard Biener <[email protected]> Date: Mon Jul 13 10:03:55 2026 +0200 tree-optimization/126194 - bogus PTA with C++ delete We currently ignore all delete calls from delete expressions for the purpose of generating PTA constraints. That's in error from r11-3612-g4f4ced28826ece which did a tree-wide change to clarify and improve eliding of new/delete pairs. The following restores the original restriction of only ignoring calls to the standard library copy of delete. This also adds a check on -fassume-sane-operators-new-delete (which is the default). PR tree-optimization/126194 * gimple-ssa-pta-constraints.cc (find_func_aliases_for_call): Only ignore calls to delete if DECL_IS_REPLACEABLE_OPERATOR. * g++.dg/lto/pr126194.h: New testcase. * g++.dg/lto/pr126194_0.C: Likewise. * g++.dg/lto/pr126194_1.C: Likewise. * g++.dg/lto/pr126194_2.C: Likewise. Diff: --- gcc/gimple-ssa-pta-constraints.cc | 2 + gcc/testsuite/g++.dg/lto/pr126194.h | 87 +++++++++++++++++++++++++++++++++++ gcc/testsuite/g++.dg/lto/pr126194_0.C | 71 ++++++++++++++++++++++++++++ gcc/testsuite/g++.dg/lto/pr126194_1.C | 41 +++++++++++++++++ gcc/testsuite/g++.dg/lto/pr126194_2.C | 8 ++++ 5 files changed, 209 insertions(+) diff --git a/gcc/gimple-ssa-pta-constraints.cc b/gcc/gimple-ssa-pta-constraints.cc index a377b891d802..b433aee8243f 100644 --- a/gcc/gimple-ssa-pta-constraints.cc +++ b/gcc/gimple-ssa-pta-constraints.cc @@ -2150,7 +2150,9 @@ find_func_aliases_for_call (struct function *fn, gcall *t) such operator, then the effects for PTA (in particular the escaping of the pointer) can be ignored. */ else if (fndecl + && flag_assume_sane_operators_new_delete && DECL_IS_OPERATOR_DELETE_P (fndecl) + && DECL_IS_REPLACEABLE_OPERATOR (fndecl) && gimple_call_from_new_or_delete (t)) ; else diff --git a/gcc/testsuite/g++.dg/lto/pr126194.h b/gcc/testsuite/g++.dg/lto/pr126194.h new file mode 100644 index 000000000000..21db1d9885d9 --- /dev/null +++ b/gcc/testsuite/g++.dg/lto/pr126194.h @@ -0,0 +1,87 @@ +#pragma once + +using Size = decltype(sizeof(0)); + +struct A +{ + void (*const f_)(void**) noexcept; + void** const p_; + Size const a_; + + A(void** pp, void (*f)(void**) noexcept, Size a) noexcept + : f_(f), p_(pp), a_(a) {} + + void reset() const noexcept + { + if (*p_) { + (*f_)(p_); + } + } + + A& operator=(const A&) = delete; + + void* create(Size size, const void* caller, struct R& r) const; + void destroy(void* p, const void* caller, struct R& r) const noexcept; +}; + +struct R +{ + virtual ~R() = default; + virtual void* allocate(Size size, const void* caller) = 0; + virtual void deallocate(void* p, const void* caller) noexcept = 0; +}; + +R& get_r() noexcept; +void touch(const void* p) noexcept; + +struct B +{ + void* q = nullptr; + + static void cb(void** pp) noexcept + { + B* self = reinterpret_cast<B*>(pp); + if (self->q) { + extern void release_q(void*); + release_q(self->q); + } + self->q = nullptr; + } + + ~B() noexcept + { + touch(this); + if (q) { + extern void release_q(void*); + release_q(q); + } + } + + B() noexcept = default; + B(const B&) = delete; + B& operator=(const B&) = delete; + B(B&& other) noexcept : q(other.q) + { + other.q = nullptr; + } + B& operator=(B&& other) noexcept + { + if (this != &other) { + if (q) { + extern void release_q(void*); + release_q(q); + } + q = other.q; + other.q = nullptr; + } + return *this; + } + + operator A() noexcept + { + return A(&q, &cb, alignof(void*)); + } +}; + +void* operator new(Size size, A const& a, R& r) noexcept(false); +void operator delete(void* p, A const& a, R& r) noexcept; diff --git a/gcc/testsuite/g++.dg/lto/pr126194_0.C b/gcc/testsuite/g++.dg/lto/pr126194_0.C new file mode 100644 index 000000000000..b7c85039862b --- /dev/null +++ b/gcc/testsuite/g++.dg/lto/pr126194_0.C @@ -0,0 +1,71 @@ +// { dg-lto-do run } +// { dg-lto-options { { -O2 -flto } } } + +#include "pr126194.h" + +extern "C" void* malloc(Size size) noexcept; +extern "C" void free(void* p) noexcept; + +namespace { +class M final : public R +{ +public: + void* allocate(Size size, const void* /*caller*/) override + { + return malloc(size); + } + + void deallocate(void* p, const void* /*caller*/) noexcept override + { + free(p); + } +}; + +M g_r; +} + +R& get_r() noexcept +{ + return g_r; +} + +void touch(const void* p) noexcept +{ + static const volatile void* sink = nullptr; + sink = p; +} + +void release_q(void* p) +{ + free(p); +} + +int g_should_throw = 0; + +void* A::create(Size size, const void* caller, R& r) const +{ + reset(); + if (size < a_) size = a_; + void* p = r.allocate(size, caller); + if (!p) throw 2; + *p_ = p; + return p; +} + +__attribute__((noinline)) +void A::destroy(void* p, const void* caller, R& r) const noexcept +{ + *p_ = nullptr; + r.deallocate(p, caller); +} + +void* operator new(Size size, A const& a, R& r) noexcept(false) +{ + return a.create(size, __builtin_return_address(0), r); +} + +__attribute__((noinline)) +void operator delete(void* p, A const& a, R& r) noexcept +{ + a.destroy(p, __builtin_return_address(0), r); +} diff --git a/gcc/testsuite/g++.dg/lto/pr126194_1.C b/gcc/testsuite/g++.dg/lto/pr126194_1.C new file mode 100644 index 000000000000..e2894cd0105b --- /dev/null +++ b/gcc/testsuite/g++.dg/lto/pr126194_1.C @@ -0,0 +1,41 @@ +#include "pr126194.h" + +extern int g_should_throw; + +struct T +{ + int a; + __attribute__((noinline)) T() : a(0) + { + if (g_should_throw) { + throw 1; + } + } +}; + +__attribute__((noinline)) +int make_widget(bool go, void** out_ptr) +{ + B b; + R& r = get_r(); + if (go) { + T* t = new(b, r) T(); + (void)t; + } + *out_ptr = b.q; + b.q = nullptr; + return 42; +} + +extern "C" __attribute__((visibility("default"))) +int run_repro(int argc) +{ + g_should_throw = (argc > 0); + void* p = nullptr; + try { + (void)make_widget(argc > 0, &p); + return 1; + } catch (...) { + return 0; + } +} diff --git a/gcc/testsuite/g++.dg/lto/pr126194_2.C b/gcc/testsuite/g++.dg/lto/pr126194_2.C new file mode 100644 index 000000000000..3c024f47a196 --- /dev/null +++ b/gcc/testsuite/g++.dg/lto/pr126194_2.C @@ -0,0 +1,8 @@ +// { dg-options "-fno-lto" } + +extern "C" int run_repro(int argc); + +int main(int argc, char**) +{ + return run_repro(argc); +}
