]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
Add -f{,no-}assume-sane-operators-new-delete options [PR110137]
authorJakub Jelinek <jakub@redhat.com>
Fri, 22 Nov 2024 18:52:35 +0000 (19:52 +0100)
committerJakub Jelinek <jakub@gcc.gnu.org>
Fri, 22 Nov 2024 18:52:35 +0000 (19:52 +0100)
The following patch adds a new option for optimizations related to
replaceable global operators new/delete.
The option isn't called -fassume-sane-operator-new (which clang++
implements), because
1) clang++ option means something different; initially it was an
   option to add malloc attribute to those declarations (but we have
   malloc attribute on all <new> calls already unconditionally);
   later it was changed to add noalias attribute rather than malloc,
   whatever it means, but it is certainly about the return value
   from the operator new (whether it can alias with other pointers);
   we already assume malloc-ish behavior that it doesn't alias any
   other pointers
2) the option only affects operator new, we want it affect also
   operator delete
The option basically allows to choose between pre-PR101480 behavior
(now the default, more optimistic) and post-PR101480 behavior (safer
but penalizing most of the code in the wild for rare needs).

I've tried to explain stuff in the documentation too.

2024-11-22  Jakub Jelinek  <jakub@redhat.com>

PR c++/110137
PR middle-end/101480
gcc/
* doc/invoke.texi (-fassume-sane-operators-new-delete,
-fno-assume-sane-operators-new-delete): Document.
* gimple.cc (gimple_call_fnspec): Handle
-f{,no-}assume-sane-operators-new-delete.
* ipa-inline-transform.cc (inline_call): Also clear
flag_assume_sane_operators_new_delete on caller when inlining
-fno-assume-sane-operators-new-delete callee into
-fassume-sane-operators-new-delete caller.
gcc/c-family/
* c.opt (fassume-sane-operators-new-delete): New option.
gcc/testsuite/
* g++.dg/tree-ssa/pr110137-1.C: New test.
* g++.dg/tree-ssa/pr110137-2.C: New test.
* g++.dg/tree-ssa/pr110137-3.C: New test.
* g++.dg/tree-ssa/pr110137-4.C: New test.
* g++.dg/torture/pr10148.C: Add -fno-assume-sane-operators-new-delete
as dg-additional-options.
* g++.dg/warn/Warray-bounds-16.C: Revert 2021-11-10 changes.

gcc/c-family/c.opt
gcc/doc/invoke.texi
gcc/gimple.cc
gcc/ipa-inline-transform.cc
gcc/testsuite/g++.dg/torture/pr10148.C
gcc/testsuite/g++.dg/tree-ssa/pr110137-1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/tree-ssa/pr110137-2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/tree-ssa/pr110137-3.C [new file with mode: 0644]
gcc/testsuite/g++.dg/tree-ssa/pr110137-4.C [new file with mode: 0644]
gcc/testsuite/g++.dg/warn/Warray-bounds-16.C

index 65cb8e5c6969b244bed75b39835c84088156795c..268725471329972a181155e834fa1cb56b2374f1 100644 (file)
@@ -1690,6 +1690,10 @@ fasm
 C ObjC C++ ObjC++ Var(flag_no_asm, 0)
 Recognize the \"asm\" keyword.
 
+fassume-sane-operators-new-delete
+C++ ObjC++ Optimization Var(flag_assume_sane_operators_new_delete) Init(1)
+Assume C++ replaceable global operators new, new[], delete, delete[] don't read or write visible global state.
+
 ; Define extra predefined macros for use in libgcc.
 fbuilding-libgcc
 C ObjC C++ ObjC++ Undocumented Var(flag_building_libgcc)
index 0951901f50af7d5aac4e664c5619affa1f5333c7..a8662efb5cb203b42847af6021121ed48e3c2ef5 100644 (file)
@@ -213,7 +213,9 @@ in the following sections.
 @item C++ Language Options
 @xref{C++ Dialect Options,,Options Controlling C++ Dialect}.
 @gccoptlist{-fabi-version=@var{n}  -fno-access-control
--faligned-new=@var{n}  -fargs-in-order=@var{n}  -fchar8_t  -fcheck-new
+-faligned-new=@var{n}  -fargs-in-order=@var{n}
+-fno-assume-sane-operators-new-delete
+-fchar8_t  -fcheck-new
 -fconcepts  -fconstexpr-depth=@var{n}  -fconstexpr-cache-depth=@var{n}
 -fconstexpr-loop-limit=@var{n}  -fconstexpr-ops-limit=@var{n}
 -fno-elide-constructors
@@ -3163,6 +3165,35 @@ but few users will need to override the default of
 
 This flag is enabled by default for @option{-std=c++17}.
 
+@opindex fno-assume-sane-operators-new-delete
+@opindex fassume-sane-operators-new-delete
+@item -fno-assume-sane-operators-new
+The C++ standard allows replacing the global @code{new}, @code{new[]},
+@code{delete} and @code{delete[]} operators, though a lot of C++ programs
+don't replace them and just use the implementation provided version.
+Furthermore, the C++ standard allows omitting those calls if they are
+made from new or delete expressions (and by extension the same is
+assumed if @code{__builtin_operator_new} or @code{__builtin_operator_delete}
+functions are used).
+This option allows control over some optimizations around calls
+to those operators.
+With @code{-fassume-sane-operators-new-delete} option GCC may assume that
+calls to the replaceable global operators from new or delete expressions or
+from @code{__builtin_operator_new} or @code{__builtin_operator_delete} calls
+don't read or modify any global variables or variables whose address could
+escape to the operators (global state; except for @code{errno} for the
+@code{new} and @code{new[]} operators).
+This allows most optimizations across those calls and is something that
+the implementation provided operators satisfy unless @code{malloc}
+implementation details are observable in the code or unless @code{malloc}
+hooks are used, but might not be satisfied if a program replaces those
+operators.  This behavior is enabled by default.
+With @code{-fno-assume-sane-operators-new-delete} option GCC must
+assume all these calls (whether from new or delete expressions or called
+directly) may read and write global state unless proven otherwise (e.g.@:
+when GCC compiles their implementation).  Use this option if those
+operators are or may be replaced and code needs to expect such behavior.
+
 @opindex fchar8_t
 @opindex fno-char8_t
 @item -fchar8_t
index add4a645b454c07be8ff52f7d16d9424fc1336dd..6e8ac05c8776401177312bbbe90d5d8ec89359d0 100644 (file)
@@ -1615,12 +1615,22 @@ gimple_call_fnspec (const gcall *stmt)
       && DECL_IS_OPERATOR_DELETE_P (fndecl)
       && DECL_IS_REPLACEABLE_OPERATOR (fndecl)
       && gimple_call_from_new_or_delete (stmt))
-    return ". o ";
+    {
+      if (flag_assume_sane_operators_new_delete)
+       return ".co ";
+      else
+       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 ";
+    {
+      if (flag_assume_sane_operators_new_delete)
+       return "mC";
+      else
+       return "m ";
+    }
   return "";
 }
 
index bf34e172b7be4fb8ba1ebe86ef5945f41071e2c4..cfa902b4dd3f42c42b5e95479f87d95682f56c55 100644 (file)
@@ -391,17 +391,33 @@ inline_call (struct cgraph_edge *e, bool update_original,
       = DECL_FUNCTION_PERSONALITY (callee->decl);
 
   bool reload_optimization_node = false;
-  if (!opt_for_fn (callee->decl, flag_strict_aliasing)
-      && opt_for_fn (to->decl, flag_strict_aliasing))
+  bool remove_strict_aliasing
+    = (!opt_for_fn (callee->decl, flag_strict_aliasing)
+       && opt_for_fn (to->decl, flag_strict_aliasing));
+  bool remove_assume_sane_operators_new_delete
+    = (!opt_for_fn (callee->decl, flag_assume_sane_operators_new_delete)
+       && opt_for_fn (to->decl, flag_assume_sane_operators_new_delete));
+  if (remove_strict_aliasing || remove_assume_sane_operators_new_delete)
     {
       struct gcc_options opts = global_options;
       struct gcc_options opts_set = global_options_set;
 
       cl_optimization_restore (&opts, &opts_set, opts_for_fn (to->decl));
-      opts.x_flag_strict_aliasing = false;
-      if (dump_file)
-       fprintf (dump_file, "Dropping flag_strict_aliasing on %s\n",
-                to->dump_name ());
+      if (remove_strict_aliasing)
+       {
+         opts.x_flag_strict_aliasing = false;
+         if (dump_file)
+           fprintf (dump_file, "Dropping flag_strict_aliasing on %s\n",
+                    to->dump_name ());
+       }
+      if (remove_assume_sane_operators_new_delete)
+       {
+         opts.x_flag_assume_sane_operators_new_delete = false;
+         if (dump_file)
+           fprintf (dump_file,
+                    "Dropping flag_assume_sane_operators_new_delete on %s\n",
+                    to->dump_name ());
+       }
       DECL_FUNCTION_SPECIFIC_OPTIMIZATION (to->decl)
         = build_optimization_node (&opts, &opts_set);
       reload_optimization_node = true;
index b5483f3ca315e928815980b20e0a593bf86341e3..23a53916ebd1af679ceb33ca26cc6b6ee466fd07 100644 (file)
@@ -1,5 +1,6 @@
 /* { dg-do run } */
 /* { dg-skip-if "requires hosted libstdc++ for stdlib malloc" { ! hostedlib } } */
+/* { dg-additional-options "-fno-assume-sane-operators-new-delete" } */
 
 #include <stdlib.h>
 #include <assert.h>
diff --git a/gcc/testsuite/g++.dg/tree-ssa/pr110137-1.C b/gcc/testsuite/g++.dg/tree-ssa/pr110137-1.C
new file mode 100644 (file)
index 0000000..18f8d23
--- /dev/null
@@ -0,0 +1,74 @@
+// PR c++/110137
+// { dg-do compile }
+// { dg-options "-O2 -fdump-tree-optimized" }
+// { dg-final { scan-tree-dump "j = 2;" "optimized" } } */
+// { dg-final { scan-tree-dump "m = 2;" "optimized" } } */
+// { dg-final { scan-tree-dump-not "q = 2;" "optimized" } } */
+// { dg-final { scan-tree-dump-not "t = 2;" "optimized" } } */
+// { dg-final { scan-tree-dump-not "k = 1;" "optimized" } } */
+// { dg-final { scan-tree-dump-times "r = 1;" 2 "optimized" } } */
+
+int i, j, k, l, m, n, o, q, r, s, t, u;
+
+void *
+foo ()
+{
+  i = 1;
+  j = 2;
+  void *p = ::operator new (32);
+  j = 3;
+  k = i;
+  return p;
+}
+
+void
+bar (void *p)
+{
+  l = 1;
+  m = 2;
+  ::operator delete (p);
+  m = 3;
+  n = l;
+}
+
+int *
+baz ()
+{
+  o = 1;
+  q = 2;
+  int *p = new int;
+  q = 3;
+  r = o;
+  return p;
+}
+
+void
+qux (int *p)
+{
+  s = 1;
+  t = 2;
+  delete p;
+  t = 3;
+  u = s;
+}
+
+void *
+corge ()
+{
+  o = 1;
+  q = 2;
+  void *p = __builtin_operator_new (32);
+  q = 3;
+  r = o;
+  return p;
+}
+
+void
+waldo (void *p)
+{
+  s = 1;
+  t = 2;
+  __builtin_operator_delete (p);
+  t = 3;
+  u = s;
+}
diff --git a/gcc/testsuite/g++.dg/tree-ssa/pr110137-2.C b/gcc/testsuite/g++.dg/tree-ssa/pr110137-2.C
new file mode 100644 (file)
index 0000000..1bb3eab
--- /dev/null
@@ -0,0 +1,74 @@
+// PR c++/110137
+// { dg-do compile }
+// { dg-options "-O2 -fdump-tree-optimized -fassume-sane-operators-new-delete" }
+// { dg-final { scan-tree-dump "j = 2;" "optimized" } } */
+// { dg-final { scan-tree-dump "m = 2;" "optimized" } } */
+// { dg-final { scan-tree-dump-not "q = 2;" "optimized" } } */
+// { dg-final { scan-tree-dump-not "t = 2;" "optimized" } } */
+// { dg-final { scan-tree-dump-not "k = 1;" "optimized" } } */
+// { dg-final { scan-tree-dump-times "r = 1;" 2 "optimized" } } */
+
+int i, j, k, l, m, n, o, q, r, s, t, u;
+
+void *
+foo ()
+{
+  i = 1;
+  j = 2;
+  void *p = ::operator new (32);
+  j = 3;
+  k = i;
+  return p;
+}
+
+void
+bar (void *p)
+{
+  l = 1;
+  m = 2;
+  ::operator delete (p);
+  m = 3;
+  n = l;
+}
+
+int *
+baz ()
+{
+  o = 1;
+  q = 2;
+  int *p = new int;
+  q = 3;
+  r = o;
+  return p;
+}
+
+void
+qux (int *p)
+{
+  s = 1;
+  t = 2;
+  delete p;
+  t = 3;
+  u = s;
+}
+
+void *
+corge ()
+{
+  o = 1;
+  q = 2;
+  void *p = __builtin_operator_new (32);
+  q = 3;
+  r = o;
+  return p;
+}
+
+void
+waldo (void *p)
+{
+  s = 1;
+  t = 2;
+  __builtin_operator_delete (p);
+  t = 3;
+  u = s;
+}
diff --git a/gcc/testsuite/g++.dg/tree-ssa/pr110137-3.C b/gcc/testsuite/g++.dg/tree-ssa/pr110137-3.C
new file mode 100644 (file)
index 0000000..e46cd84
--- /dev/null
@@ -0,0 +1,76 @@
+// PR c++/110137
+// { dg-do compile }
+// { dg-options "-O2 -fdump-tree-optimized -fno-assume-sane-operators-new-delete" }
+// { dg-final { scan-tree-dump "j = 2;" "optimized" } } */
+// { dg-final { scan-tree-dump "m = 2;" "optimized" } } */
+// { dg-final { scan-tree-dump-times "q = 2;" 2 "optimized" } } */
+// { dg-final { scan-tree-dump-times "t = 2;" 2 "optimized" } } */
+// { dg-final { scan-tree-dump-not "k = 1;" "optimized" } } */
+// { dg-final { scan-tree-dump-not "n = 1;" "optimized" } } */
+// { dg-final { scan-tree-dump-not "r = 1;" "optimized" } } */
+// { dg-final { scan-tree-dump-not "u = 1;" "optimized" } } */
+
+int i, j, k, l, m, n, o, q, r, s, t, u;
+
+void *
+foo ()
+{
+  i = 1;
+  j = 2;
+  void *p = ::operator new (32);
+  j = 3;
+  k = i;
+  return p;
+}
+
+void
+bar (void *p)
+{
+  l = 1;
+  m = 2;
+  ::operator delete (p);
+  m = 3;
+  n = l;
+}
+
+int *
+baz ()
+{
+  o = 1;
+  q = 2;
+  int *p = new int;
+  q = 3;
+  r = o;
+  return p;
+}
+
+void
+qux (int *p)
+{
+  s = 1;
+  t = 2;
+  delete p;
+  t = 3;
+  u = s;
+}
+
+void *
+corge ()
+{
+  o = 1;
+  q = 2;
+  void *p = __builtin_operator_new (32);
+  q = 3;
+  r = o;
+  return p;
+}
+
+void
+waldo (void *p)
+{
+  s = 1;
+  t = 2;
+  __builtin_operator_delete (p);
+  t = 3;
+  u = s;
+}
diff --git a/gcc/testsuite/g++.dg/tree-ssa/pr110137-4.C b/gcc/testsuite/g++.dg/tree-ssa/pr110137-4.C
new file mode 100644 (file)
index 0000000..705dd38
--- /dev/null
@@ -0,0 +1,124 @@
+// PR c++/110137
+// { dg-do compile }
+// { dg-options "-O2 -fdump-tree-optimized" }
+// { dg-final { scan-tree-dump "j = 2;" "optimized" } } */
+// { dg-final { scan-tree-dump "m = 2;" "optimized" } } */
+// { dg-final { scan-tree-dump-times "q = 2;" 2 "optimized" } } */
+// { dg-final { scan-tree-dump-times "t = 2;" 2 "optimized" } } */
+// { dg-final { scan-tree-dump-not "k = 1;" "optimized" } } */
+// { dg-final { scan-tree-dump-not "n = 1;" "optimized" } } */
+// { dg-final { scan-tree-dump-not "r = 1;" "optimized" } } */
+// { dg-final { scan-tree-dump-not "u = 1;" "optimized" } } */
+
+int i, j, k, l, m, n, o, q, r, s, t, u;
+
+static inline
+__attribute__((always_inline,
+              optimize ("no-assume-sane-operators-new-delete"))) void *
+foo ()
+{
+  i = 1;
+  j = 2;
+  void *p = ::operator new (32);
+  j = 3;
+  k = i;
+  return p;
+}
+
+static inline
+__attribute__((always_inline,
+              optimize ("no-assume-sane-operators-new-delete"))) void
+bar (void *p)
+{
+  l = 1;
+  m = 2;
+  ::operator delete (p);
+  m = 3;
+  n = l;
+}
+
+static inline
+__attribute__((always_inline,
+              optimize ("no-assume-sane-operators-new-delete"))) int *
+baz ()
+{
+  o = 1;
+  q = 2;
+  int *p = new int;
+  q = 3;
+  r = o;
+  return p;
+}
+
+static inline
+__attribute__((always_inline,
+              optimize ("no-assume-sane-operators-new-delete"))) void
+qux (int *p)
+{
+  s = 1;
+  t = 2;
+  delete p;
+  t = 3;
+  u = s;
+}
+
+static inline
+__attribute__((always_inline,
+              optimize ("no-assume-sane-operators-new-delete"))) void *
+corge ()
+{
+  o = 1;
+  q = 2;
+  void *p = __builtin_operator_new (32);
+  q = 3;
+  r = o;
+  return p;
+}
+
+static inline
+__attribute__((always_inline,
+              optimize ("no-assume-sane-operators-new-delete"))) void
+waldo (void *p)
+{
+  s = 1;
+  t = 2;
+  __builtin_operator_delete (p);
+  t = 3;
+  u = s;
+}
+
+void *
+foo2 ()
+{
+  return foo ();
+}
+
+void
+bar2 (void *p)
+{
+  bar (p);
+}
+
+int *
+baz2 ()
+{
+  return baz ();
+}
+
+void
+qux2 (int *p)
+{
+  qux (p);
+}
+
+void *
+corge2 ()
+{
+  return corge ();
+}
+
+void
+waldo2 (void *p)
+{
+  waldo (p);
+}
index 89cbadb91c7b91fb8422624ec2454d0e60768b77..17b4d0d194e76733e81be3438b121885265a9ffa 100644 (file)
@@ -19,11 +19,11 @@ struct S
     p = (int*) new unsigned char [sizeof (int) * m];
 
     for (int i = 0; i < m; i++)
-      new (p + i) int (); /* { dg-bogus "bounds" "pr102690" { xfail *-*-* } } */
+      new (p + i) int ();
   }
 };
 
 S a (0);
 
-/* The loop cannot be eliminated since the global 'new' can change 'm'.  */
-/* { dg-final { scan-tree-dump-not "goto" "optimized" { xfail *-*-* } } } */
+/* Verify the loop has been eliminated.
+   { dg-final { scan-tree-dump-not "goto" "optimized" } } */