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)
@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
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
&& 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 "";
}
= 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;
/* { 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>
--- /dev/null
+// 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;
+}
--- /dev/null
+// 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;
+}
--- /dev/null
+// 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;
+}
--- /dev/null
+// 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);
+}
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" } } */